https://wiki.brasilpeeringforum.org/api.php?action=feedcontributions&user=Gondim&feedformat=atomWiki BPF - Contribuições do(a) usuário(a) [pt-br]2024-03-29T01:26:27ZContribuições do(a) usuário(a)MediaWiki 1.31.0https://wiki.brasilpeeringforum.org/index.php?title=DNS_Recursivo_Anycast_Hyperlocal&diff=3679DNS Recursivo Anycast Hyperlocal2024-03-12T16:45:05Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
==Introdução==<br />
Você sabe como funciona a Internet? Essa é uma pergunta que meu amigo '''Thiago Ayub''' sempre faz aos seus candidatos à vagas de emprego e não importa o quanto tenham de experiência em '''Engenharia de Redes''', todos sempre travam nesse momento. Todos estão sempre prontos e preparados para resolver os problemas mais cabeludos em '''BGP''', '''OSPF''', '''MPLS''', etc mas travam com essa simples pergunta. Para contextualizar e visualizarmos melhor vamos nos atentar à imagem abaixo e uma explicação simplificada de como funciona:<br />
[[Arquivo:Dns hierarquia.png|esquerda|commoldura]]<br />
Tudo começa com um usuário sentado confortavelmente e querendo acessar um conteúdo disponível na Internet. Ele digita em seu navegador preferido a URL: '''<nowiki>https://wiki.brasilpeeringforum.org</nowiki>''',<br />
<br />
'''<big>1)</big>''' <big>O</big> <big>navegador irá requisitar do '''DNS Recursivo''' utilizado pelo usuário, o '''endereço IP''' que responde pelo nome '''wiki.'''</big>'''brasilpeeringforum.org'''<big>. Isso porque todos os acessos se dão na Internet através do '''endereço''' '''IP''' e não através do '''nome'''. Imaginem se tivéssemos que decorar os endereços IPs de todos os sites e serviços que quiséssemos acessar na Internet?</big> <br />
<br />
<big>'''2)''' Nosso DNS Recursivo checa se a informação consta em seu cache.</big> Se a informação existir ela é devolvida ao navegador do usuário e aí este consegue acessar o site.<br />
<br />
'''3)''' Do contrário o DNS Recursivo pergunta ao '''Root Server''' quem é o '''TLD (Top Level Domain)''' responsável para atender a requisição. <br />
<br />
'''4)''' O '''Root Server''' informa ao DNS Recursivo o endereço do '''TLD responsável'''. No Brasil o '''TLD''' responsável pelo '''.br''' seria o '''Registro.br'''.<br />
<br />
'''5)''' O DNS Recursivo pergunta ao '''TLD''' sobre '''wiki.brasilpeeringforum.org''' e este responde com os endereços IP dos '''DNS Autoritativos''' responsáveis pelo domínio '''brasilpeeringforum.org.'''<br />
<br />
'''6)''' O DNS Recursivo pergunta aos '''DNS Autoritativos''' pelo '''wiki.brasilpeeringforum.org''' e este responde com o '''endereço IP'''.<br />
<br />
'''7)''' Por último o DNS Recursivo devolve a informação para o navegador do usuário.<br />
<br />
Como que se dá a comunicação entre os '''DNS(s) Recursivos, Root Servers, TLDs''' e '''Autoritativos'''? Como que o navegador do usuário, após receber o IP do site, consegue chegar no servidor que tem o conteúdo? Isso só é possível devido ao protocolo chamado '''BGP (Border Gateway Protocol)''', todos os caminhos que conhecemos como rotas de destino, são anunciadas por milhares de participantes na '''Internet''' conhecidos como '''AS (Autonomous System)''', esses participantes se interligam para disponibilizar conteúdos e acessos pelo mundo aos milhares de usuários. É uma imensa rede colaborativa formada por Empresas, Universidades, Governos e todos que queiram se interconectar. Percebam que sem o '''BGP''', que serve de caminho para chegarmos nos conteúdos e sem o '''DNS (Domain Name System)''' para traduzir o nome para o endereço IP, a '''Internet''' não funcionaria e por isso precisamos cuidar muito bem desses dois serviços.<br />
<br><br><br><br><br />
Mas não acaba por aí. O '''DNS Recursivo''' tem um papel muito importante para o Provedor de Internet e que envolve segurança, qualidade de acesso à Internet e a disponibilidade do serviço entregue ao cliente. Quando bem configurado acelera as consultas dos acessos graças ao seu cache interno, mas para que isso seja percebido pelo assinante, é necessário que esteja o mais próximo possível do seu cliente.<br />
<br><br />
== Um erro que destrói a qualidade do nosso serviço ==<br />
Um erro muito comum que muitas operadoras cometem é utilizar DNS Recursivo externo, como o '''8.8.8.8''', '''1.1.1.1''' e outros, para seus clientes. Quanto mais próximo dos seus clientes, mais qualidade de serviço estará entregando a eles. Conteúdos serão entregues mais rapidamente pois serão resolvidos e armazenados em caches locais e não consultados remotamente na Internet. Para falar mais sobre isso, te convido leitor desse documento, que assista essa palestra do '''Thiago Ayub''' no '''GTER 51/GTS 37''' (2022) '''8.888 MOTIVOS PARA NÃO USAR DNS RECURSIVO EXTERNO EM SEU AS''': https://www.youtube.com/watch?v=Rsvpu5uF2Io<br />
<br />
== Objetivo ==<br />
O objetivo desta documentação não é te ensinar tudo sobre '''DNS''', '''BGP''', '''OSPF''' e nem tão pouco sobre GNU/Linux e sim te mostrar um exemplo de servidor DNS Recursivo implementado pensando em segurança, qualidade e resiliência. Usaremos em todas as nossas documentações o [https://www.debian.org/ Debian GNU/Linux], por ser uma distribuição que considero uma obra de arte criada por uma enorme comunidade séria, com vasta experiência de anos, qualidade no empacotamento dos programas, estável e com uma equipe de segurança excelente e ativa. Caso você leitor, utilize alguma outra distribuição GNU/Linux, todo conteúdo apresentado aqui pode ser aplicado em outras distros, desde que respeitando as particularidades de cada uma.<br />
<br />
Aqui construiremos um sistema do tipo '''Anycast''', ou seja, terás o serviço rodando em diversas localidades da sua Rede utilizando o mesmo endereçamento IP e que atenderá seu cliente mais próximo. Em caso de falhas, seus clientes automaticamente e de forma transparente continuarão consultando o DNS mais próximo deles. Para que ele funcione dessa forma você precisará ter uma '''Rede OSPF''' implementada no seu Provedor Internet ou algum outro protocolo como por exemplo o '''ISIS,''' mas esse documento não irá abordar o '''ISIS'''. Também utilizaremos o '''Hyperlocal''' como recurso adicional para gerar algumas proteções de segurança e velocidade na resposta relacionada aos servidores de DNS Raiz da Internet.<br />
<br />
== Diagrama ==<br />
Para exemplificar nosso servidor de DNS Recursivo, usaremos como base das explicações um diagrama demonstrando o uso do DNS Recursivo em uma Rede fictícia. Adotaremos IPs privados e reservados para demonstrar todo o ambiente do Provedor de Internet.<br />
[[Arquivo:Diagrama dns recursivo.drawio.png|esquerda|commoldura]]<br />
Nesse diagrama podemos observar alguns detalhes técnicos como por exemplo: existem '''3 servidores de DNS Recursivo''' posicionados em locais diferentes, que poderiam estar em bairros diferentes e até em cidades diferentes. Em cada servidor teremos '''2 loopbacks''' com os IPs:<br />
<br />
'''10.10.10.10/32'''<br />
<br />
'''10.10.9.9/32'''<br />
<br />
Esses IPs serão entregues pelos concentradores '''PPPoE''' ou '''IPoE''' ('''BNG''') para seus clientes como '''DNS primário''' e '''secundário'''. Podemos usar IPs privados como DNS primário e secundário em um ambiente real? Sim podemos, desde que não sejam IPs que possam ter problemas com as redes privadas dos clientes. Ex.: rede do cliente usando '''192.168.0.0/24'''. Se entregarmos o DNS sendo '''192.168.0.10''' e '''192.168.0.20''' teremos problemas e o cliente ficará sem Internet, porque '''192.168.0.10''' e '''192.168.0.20''' fazem parte da rede '''192.168.0.0/24'''.<br />
<br />
Agora entregando '''10.10.10.10''' e '''10.10.9.9''' não teríamos problemas com a rede '''192.168.0.0/24'''.<br />
<br />
'''Motivos para usarmos IPs privados:'''<br />
* O principal motivo está relacionado com a segurança, uma vez que sendo um IP privado, não pode sofrer ataques DDoS direcionados diretamente para ele, vindos da Internet.<br />
* Nem mesmo o cliente da sua rede conhece os '''IPs públicos''' utilizados para recursividade na Internet.<br />
* Memorizar os IPs '''10.10.10.10''' e '''10.10.9.9''' é tão fácil quanto memorizar o '''8.8.8.8''' e o '''1.1.1.1'''. Mais fácil para o seu técnico guardar essa informação e utilizar onde for necessário.<br />
Cada servidor DNS Recursivo possui um '''IPv4 público''', aqui representado por '''198.18.x.x/27''' e um '''IPv6 global''' representado por um IP dentro do prefixo '''2001:db8::/32'''. Cada servidor precisa ter os seus próprios IPs e são através destes IPs que as consultas de DNS serão realizadas na Internet.<br />
<br />
Nessa topologia usando '''Anycast''', o cliente será sempre atendido pelo '''DNS Recursivo''' mais próximo, desde que os pesos no '''OSPF''' estejam ajustados corretamente.<br />
<br><br><br><br><br><br><br />
== Dados do servidor ==<br />
Podemos utilizar um sistema virtualizado ou não. Sistemas virtualizados são bem vindos pois são mais simples quando precisamos fazer backups, levantar outros sistemas sem complicações e se precisarmos restaurar rapidamente algum sistema que ficou indisponível por algum motivo. A configuração abaixo tem capacidade para atender algo em torno a '''50.000 assinantes ou mais'''. O DNS Recursivo é um serviço que pode ser utilizado até mesmo em um '''Raspberry Pi''' e atender operações pequenas, nesse caso com o intuito de economizar energia e espaço. Nosso foco aqui é montar uma rede de '''DNS Recursivo Anycast com HyperLocal'''. Como comentei acima o servidor deve ficar o mais próximo dos clientes para termos a '''menor latência possível''' e '''sempre menor que 5ms''' entre o cliente e o servidor.<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Memória<br />
!Disco<br />
!Sistema<br />
|-<br />
|2.4Ghz 4 cores<br />
|16G DDR4<br />
|30G<br />
|Debian 11 amd64 (Bullseye)<br />
|}<br />
<br />
== Softwares utilizados ==<br />
* Debian 11 amd64 (Bullseye) instalação mínima.<br />
<br />
* [https://frrouting.org/ FRRouting].<br />
* Unbound.<br />
* IRQBalance.<br />
* Chrony (NTP/NTS).<br />
* Shell script em bash.<br />
<br />
== Funcionalidades que teremos ==<br />
* Sistema em Anycast.<br />
* Hyperlocal.<br />
* Controle de acesso por <abbr>ACL</abbr>.<br />
* RPZ (Response Policy Zone).<br />
* Bloqueio de consultas do tipo ANY.<br />
* QNAME minimization habilitado. (habilitado por default no Unbound)<br />
* Recursividade em IPv4 e IPv6.<br />
* DNSSEC habilitado.<br />
* <abbr>DoH (DNS</abbr> over HTTPS) habilitado.<br />
<br />
== Monitoramento ==<br />
O monitoramento é algo bem específico e não é o foco deste documento mas é extremamente importante que você monitore seus servidores de DNS por alguma ferramenta como o Zabbix. Aqui mostrarei apenas como enviar as informações para o Zabbix. Algumas coisas que você deveria monitorar nos servidores de DNS Recursivo:<br />
* Serviço do unbound parou.<br />
* Perda de pacotes.<br />
* Latência alta de pacotes.<br />
* Lentidão na resolução de queries.<br />
* CPU alta.<br />
* Load alto.<br />
* Memória com uso alto.<br />
* Disco com pouco espaço.<br />
* Queda brusca nas queries.<br />
* A recursividade parou de funcionar.<br />
* A recursividade voltou a funcionar.<br />
Este abaixo é um exemplo de monitoramento de um sistema de DNS Recursivo que atende 50.000 assinantes:<br />
[[Arquivo:Grafana dns.png|nenhum|commoldura]]<br />
<br />
== Configurando a Rede ==<br />
Nossa documentação será baseada no diagrama apresentado acima e por isso configuraremos apenas um dos três servidores, porque os outros serão configurados da mesma forma, só que com dados diferentes. Para tanto assumirei que já temos um sistema Debian instalado com o mínimo de pacotes e somente com sshd, para que possamos acessar remotamente mais tarde. '''Não instale um ambiente gráfico no servidor''', você não deve querer fazer isso por diversos motivos e os principais: primeiro porque não é um Desktop e segundo porque o ambiente gráfico devoraria toda a memória com recursos que não seriam úteis aqui.<br />
<br />
Em '''/etc/network/interfaces''' deixaremos assim:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto lo:0<br />
iface lo:0 inet static<br />
address 10.10.10.10/32<br />
<br />
auto lo:1<br />
iface lo:1 inet static<br />
address 10.10.9.9/32<br />
<br />
# The primary network interface<br />
auto ens18<br />
iface ens18 inet static<br />
address 198.18.1.10/27<br />
gateway 198.18.1.1<br />
<br />
iface ens18 inet6 static<br />
address 2001:db8::faca:198:18:1:10/64<br />
gateway 2001:db8::faca:198:18:1:1<br />
<br />
# The secondary network interface<br />
auto ens18:0<br />
iface ens18:0 inet static<br />
address 172.16.0.6/30<br />
Nesse cenário temos as duas '''loopbacks''' com os IPs '''10.10.10.10''' e '''10.10.9.9''' que serão anunciados via OSPF para a rede e serem entregues aos clientes via BNG. Os IPs '''198.18.1.10''' e '''2001:db8::faca:198:18:1:10''' serão usados para fazerem a recursividade na Internet tanto em IPv4 quanto em IPv6. Esses IPs não devem ser divulgados para clientes; os IPs públicos são dedicados apenas para essa finalidade.<br />
<br />
== Configuração dos repositórios Debian ==<br />
Deixe o arquivo '''/etc/apt/sources.list''' conforme abaixo:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
Após a configuração vamos instalar alguns pacotes necessários e outros úteis:<br />
# apt update && apt full-upgrade<br />
# apt install net-tools nftables htop iotop sipcalc tcpdump curl gnupg rsync wget host dnsutils mtr-tiny bmon sudo tmux whois ethtool dnstop<br />
<br />
== Fazendo algum tuning no sistema ==<br />
Em '''/etc/sysctl.conf''' adicionamos no final do arquivo essas instruções:<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
Estamos fazendo algumas melhorias de memória, algumas relacionadas a '''conntrack''' porque se for usar um filtro de pacotes stateful, como o '''Netfilter/IPTables''' ou '''Netfilter/NFTables''', o valor default da tabela é pequeno e dependendo da situação, se estourar essa tabela, as consultas de DNS terão problemas também. O DNS Recursivo não deve ficar aberto para qualquer um na Internet, ele deve ser liberado apenas para seus clientes. Podemos fazer através das ACLs do Unbound e pelo filtro de pacotes. O último parâmetro diz respeito ao uso de swap, por padrão o Debian permite o uso de swap após 40% do uso da memória, nesse caso estamos dizendo para o sistema usar o swap com 90% de uso da memória.<br />
<br />
Precisamos adicionar o módulo '''nf_conntrack''' em '''/etc/modules''' para que seja carregado em tempo de boot, senão os parâmetros de '''conntrack''' que colocamos em '''/etc/sysctl.conf''' não serão carregados.<br />
# echo nf_conntrack >> /etc/modules<br />
# modprobe nf_conntrack<br />
# sysctl -p<br />
<br />
== Instalando o FRRouting ==<br />
O FRRouting é o programa que usaremos para fazer os anúncios das nossas loopbacks via OSPF. Nesse documento usaremos a versão 8.x e para isso precisaremos configurar o repositório oficial do FRRouting e instalar os pacotes:<br />
# echo "deb <nowiki>https://deb.frrouting.org/frr</nowiki> bullseye frr-8" > /etc/apt/sources.list.d/frr.list<br />
# curl -s <nowiki>https://deb.frrouting.org/frr/keys.asc</nowiki> | apt-key add -<br />
# apt update<br />
# apt install frr frr-doc frr-pythontools<br />
Aconselho depois de instalar os pacotes, marcá-los para não atualizar juntamente com os demais pacotes, isso é para evitar de ocorrer alguma atualização no FRRouting, que torne o serviço instável por algum motivo. Não que isso vá ocorrer, mas é melhor fazer essa atualização quando realmente for necessário.<br />
# apt-mark hold frr frr-doc frr-pythontools<br />
Após esse comando acima, o sistema manterá a instalação original do pacote intacta. Para desbloquear basta executar o comando abaixo:<br />
# apt-mark unhold frr frr-doc frr-pythontools<br />
<br />
== Removendo o APPARMOR ==<br />
O '''APPARMOR''' às vezes causa mais problemas que solução e se não for fazer uma completa configuração nele, é melhor desabilitá-lo. Para fazer isso efetivamente, o procedimento é esse abaixo:<br />
# mkdir -p /etc/default/grub.d<br />
# echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=0"' | tee /etc/default/grub.d/apparmor.cfg<br />
# update-grub<br />
# reboot<br />
<br />
== Instalando o Unbound ==<br />
Nesse momento ainda não iremos configurar o Unbound, apenas instalar o pacote e acertar o ambiente. Vamos instalar o unbound do backports porque este já possui suporte ao DoH que veremos mais à frente.<br />
# apt -t bullseye-backports install unbound dns-root-data<br />
# mkdir -p /var/log/unbound<br />
# touch /var/log/unbound/unbound.log<br />
# chown -R unbound:unbound /var/log/unbound/<br />
# systemctl restart unbound<br />
Configurando o logrotate:<br />
cat << EOF > /etc/logrotate.d/unbound<br />
/var/log/unbound/unbound.log {<br />
rotate 5<br />
weekly<br />
postrotate<br />
unbound-control log_reopen<br />
endscript<br />
}<br />
EOF<br />
Reiniciando o serviço:<br />
# systemctl restart logrotate.service<br />
<br />
== Preparando o monitoramento do seu DNS Recursivo ==<br />
O monitoramento do seu DNS Recursivo é muito importante e para isso vamos usar um '''template para Zabbix''', que modifiquei juntamente com o seu shell script e que enviará os dados para o seu Zabbix server via '''zabbix-sender'''. O projeto original está aqui '''https://github.com/jeftedelima/Unbound-DNS<nowiki/>.''' O xml alterado está aqui '''https://github.com/gondimcodes/template_zabbix_dns_unbound'''. Embora seja antigo é perfeitamente importável no Zabbix 6.0, por exemplo.<br />
<br />
'''<nowiki/>'''<br />
<br />
Teremos um shell script que você precisará colocar no seu '''/etc/crontab'''. No exemplo abaixo assumi que o shell script está em '''/root/scripts'''. De 5 em 5 minutos os dados serão enviados para o seu Zabbix server.<br />
*/5 * * * * root /root/scripts/unboundSend.sh '''IP_zabbix_server''' '''nome_do_host''' 1> /dev/null<br />
Na linha acima, troque o '''IP_zabbix_server''' pelo '''IP do seu servidor Zabbix''' e o '''nome_do_host''' pelo '''hostname''' '''do seu DNS Recursivo'''. Você precisará instalar o pacote '''zabbix-sender''' no seu DNS Recursivo pois ele será usado para enviar os dados para o Zabbix server.<br />
<br />
Abaixo o '''unboundSend.sh''' também alterado com inclusão de mais dados:<br />
#!/bin/bash<br />
# @Jefte de Lima Ferreira<br />
# jeftedelima at gmail dot com<br />
# CRON Example<br />
# Contributor: Marcelo Gondim - gondim at gmail dot com<br />
# */5 **** root sh /home/dir/unboundSend.sh 192.168.10.1 Unbound 1> /dev/null<br />
<br />
if [ -z ${1} ] || [ -z ${2} ] ; then<br />
echo "You need to specify the IP address of zabbix server and hostname of your DNS Unbound on zabbix"<br />
echo "Usage example: ./unboundSend.sh 192.168.10.1 UnboundServer"<br />
exit 1<br />
fi<br />
<br />
# ZABBIX_SERVER IP<br />
IP_ZABBIX=$1<br />
# NAME Unbound on Zabbix<br />
NAME_HOST=$2<br />
DIR_TEMP=/var/tmp/<br />
FILE="${DIR_TEMP}dump_unbound_control_stats.txt"<br />
unbound-control stats > ${FILE}<br />
<br />
TOTAL_NUM_QUERIES=$(cat ${FILE} | grep -w 'total.num.queries' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEHITS=$(cat ${FILE} | grep -w 'total.num.cachehits' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEMISS=$(cat ${FILE} | grep -w 'total.num.cachemiss' | cut -d '=' -f2)<br />
TOTAL_NUM_PREFETCH=$(cat ${FILE} | grep -w 'total.num.prefetch' | cut -d '=' -f2)<br />
TOTAL_NUM_RECURSIVEREPLIES=$(cat ${FILE} | grep -w 'total.num.recursivereplies' | cut -d '=' -f2)<br />
<br />
TOTAL_REQ_MAX=$(cat ${FILE} | grep -w 'total.requestlist.max' | cut -d '=' -f2)<br />
TOTAL_REQ_AVG=$(cat ${FILE} | grep -w 'total.requestlist.avg' | cut -d '=' -f2)<br />
TOTAL_REQ_OVERWRITTEN=$(cat ${FILE} | grep -w 'total.requestlist.overwritten' | cut -d '=' -f2)<br />
TOTAL_REQ_EXCEEDED=$(cat ${FILE} | grep -w 'total.requestlist.exceeded' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_ALL=$(cat ${FILE} | grep -w 'total.requestlist.current.all' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_USER=$(cat ${FILE} | grep -w 'total.requestlist.current.user' | cut -d '=' -f2)<br />
<br />
TOTAL_TCPUSAGE=$(cat ${FILE} | grep -w 'total.tcpusage' | cut -d '=' -f2)<br />
<br />
NUM_QUERY_TYPE_A=$(cat ${FILE} | grep -w 'num.query.type.A' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NS=$(cat ${FILE} | grep -w 'num.query.type.NS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_MX=$(cat ${FILE} | grep -w 'num.query.type.MX' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TXT=$(cat ${FILE} | grep -w 'num.query.type.TXT' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_PTR=$(cat ${FILE} | grep -w 'num.query.type.PTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_AAAA=$(cat ${FILE} | grep -w 'num.query.type.AAAA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SRV=$(cat ${FILE} | grep -w 'num.query.type.SRV' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SOA=$(cat ${FILE} | grep -w 'num.query.type.SOA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HTTPS=$(cat ${FILE} | grep -w 'num.query.type.HTTPS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TYPE0=$(cat ${FILE} | grep -w 'num.query.type.TYPE0' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_CNAME=$(cat ${FILE} | grep -w 'num.query.type.CNAME' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_WKS=$(cat ${FILE} | grep -w 'num.query.type.WKS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HINFO=$(cat ${FILE} | grep -w 'num.query.type.HINFO' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_X25=$(cat ${FILE} | grep -w 'num.query.type.X25' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NAPTR=$(cat ${FILE} | grep -w 'num.query.type.NAPTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DS=$(cat ${FILE} | grep -w 'num.query.type.DS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DNSKEY=$(cat ${FILE} | grep -w 'num.query.type.DNSKEY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TLSA=$(cat ${FILE} | grep -w 'num.query.type.TLSA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SVCB=$(cat ${FILE} | grep -w 'num.query.type.SVCB' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SPF=$(cat ${FILE} | grep -w 'num.query.type.SPF' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_ANY=$(cat ${FILE} | grep -w 'num.query.type.ANY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_OTHER=$(cat ${FILE} | grep -w 'num.query.type.other' | cut -d '=' -f2)<br />
<br />
NUM_ANSWER_RCODE_NOERROR=$(cat ${FILE} | grep -w 'num.answer.rcode.NOERROR' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_NXDOMAIN=$(cat ${FILE} | grep -w 'num.answer.rcode.NXDOMAIN' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_SERVFAIL=$(cat ${FILE} | grep -w 'num.answer.rcode.SERVFAIL' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_REFUSED=$(cat ${FILE} | grep -w 'num.answer.rcode.REFUSED' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_nodata=$(cat ${FILE} | grep -w 'num.answer.rcode.nodata' | cut -d '=' -f2)<br />
NUM_ANSWER_secure=$(cat ${FILE} | grep -w 'num.answer.secure' | cut -d '=' -f2)<br />
<br />
# Sending info to zabbix_server, if variables is not empty!<br />
[ -z ${TOTAL_NUM_QUERIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.queries -o ${TOTAL_NUM_QUERIES}<br />
[ -z ${TOTAL_NUM_CACHEHITS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachehits -o ${TOTAL_NUM_CACHEHITS}<br />
[ -z ${TOTAL_NUM_CACHEMISS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachemiss -o ${TOTAL_NUM_CACHEMISS}<br />
[ -z ${TOTAL_NUM_PREFETCH} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.prefetch -o ${TOTAL_NUM_PREFETCH}<br />
[ -z ${TOTAL_NUM_RECURSIVEREPLIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.recursivereplies -o ${TOTAL_NUM_RECURSIVEREPLIES}<br />
<br />
[ -z ${TOTAL_REQ_MAX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.max -o ${TOTAL_REQ_MAX}<br />
[ -z ${TOTAL_REQ_AVG} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.avg -o ${TOTAL_REQ_AVG}<br />
[ -z ${TOTAL_REQ_OVERWRITTEN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.overwritten -o ${TOTAL_REQ_OVERWRITTEN}<br />
[ -z ${TOTAL_REQ_EXCEEDED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.exceeded -o ${TOTAL_REQ_EXCEEDED}<br />
[ -z ${TOTAL_REQ_CURRENT_ALL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.all -o ${TOTAL_REQ_CURRENT_ALL}<br />
[ -z ${TOTAL_REQ_CURRENT_USER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.user -o ${TOTAL_REQ_CURRENT_USER}<br />
<br />
[ -z ${TOTAL_TCPUSAGE} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.tcpusage -o ${TOTAL_TCPUSAGE}<br />
<br />
[ -z ${NUM_QUERY_TYPE_A} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.a -o ${NUM_QUERY_TYPE_A}<br />
[ -z ${NUM_QUERY_TYPE_NS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ns -o ${NUM_QUERY_TYPE_NS}<br />
[ -z ${NUM_QUERY_TYPE_MX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.mx -o ${NUM_QUERY_TYPE_MX}<br />
[ -z ${NUM_QUERY_TYPE_TXT} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.txt -o ${NUM_QUERY_TYPE_TXT}<br />
[ -z ${NUM_QUERY_TYPE_PTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ptr -o ${NUM_QUERY_TYPE_PTR}<br />
[ -z ${NUM_QUERY_TYPE_AAAA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.aaaa -o ${NUM_QUERY_TYPE_AAAA}<br />
[ -z ${NUM_QUERY_TYPE_SRV} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.srv -o ${NUM_QUERY_TYPE_SRV}<br />
[ -z ${NUM_QUERY_TYPE_SOA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.soa -o ${NUM_QUERY_TYPE_SOA}<br />
[ -z ${NUM_QUERY_TYPE_HTTPS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.https -o ${NUM_QUERY_TYPE_HTTPS}<br />
[ -z ${NUM_QUERY_TYPE_TYPE0} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.type0 -o ${NUM_QUERY_TYPE_TYPE0}<br />
[ -z ${NUM_QUERY_TYPE_CNAME} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.cname -o ${NUM_QUERY_TYPE_CNAME}<br />
[ -z ${NUM_QUERY_TYPE_WKS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.wks -o ${NUM_QUERY_TYPE_WKS}<br />
[ -z ${NUM_QUERY_TYPE_HINFO} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.hinfo -o ${NUM_QUERY_TYPE_HINFO}<br />
[ -z ${NUM_QUERY_TYPE_X25} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.X25 -o ${NUM_QUERY_TYPE_X25}<br />
[ -z ${NUM_QUERY_TYPE_NAPTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.naptr -o ${NUM_QUERY_TYPE_NAPTR}<br />
[ -z ${NUM_QUERY_TYPE_DS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ds -o ${NUM_QUERY_TYPE_DS}<br />
[ -z ${NUM_QUERY_TYPE_DNSKEY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.dnskey -o ${NUM_QUERY_TYPE_DNSKEY}<br />
[ -z ${NUM_QUERY_TYPE_TLSA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.tlsa -o ${NUM_QUERY_TYPE_TLSA}<br />
[ -z ${NUM_QUERY_TYPE_SVCB} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.svcb -o ${NUM_QUERY_TYPE_SVCB}<br />
[ -z ${NUM_QUERY_TYPE_SPF} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.spf -o ${NUM_QUERY_TYPE_SPF}<br />
[ -z ${NUM_QUERY_TYPE_ANY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.any -o ${NUM_QUERY_TYPE_ANY}<br />
[ -z ${NUM_QUERY_TYPE_OTHER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.other -o ${NUM_QUERY_TYPE_OTHER}<br />
<br />
[ -z ${NUM_ANSWER_RCODE_NOERROR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NOERROR -o ${NUM_ANSWER_RCODE_NOERROR}<br />
[ -z ${NUM_ANSWER_RCODE_NXDOMAIN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NXDOMAIN -o ${NUM_ANSWER_RCODE_NXDOMAIN}<br />
[ -z ${NUM_ANSWER_RCODE_SERVFAIL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.SERVFAIL -o ${NUM_ANSWER_RCODE_SERVFAIL}<br />
[ -z ${NUM_ANSWER_RCODE_REFUSED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.REFUSED -o ${NUM_ANSWER_RCODE_REFUSED}<br />
[ -z ${NUM_ANSWER_RCODE_nodata} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.nodata -o ${NUM_ANSWER_RCODE_nodata}<br />
[ -z ${NUM_ANSWER_secure} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.secure -o ${NUM_ANSWER_secure}<br />
No Zabbix será registrado dados como esses abaixo e posteriormente pode ser montado um Grafana com eles:<br />
[[Arquivo:Zabbix dns01.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns02.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns03.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns04.png|nenhum|commoldura]]<br />
<br />
== Balanceando o processamento e mantendo a hora certa ==<br />
Vamos instalar 2 programas agora, o IRQBalance e o Chrony. O primeiro para balancear a carga entre os cores e o segundo para manter a data e hora certas no sistema:<br />
# apt install irqbalance<br />
# systemctl enable irqbalance<br />
# apt install chrony<br />
Após a instalação do Chrony edite o arquivo /etc/chrony/chrony.conf, comente e a linha abaixo e adicione seus servidores NTP. Caso não tenha servidores NTP, estou colocando os do NIC.br aqui.<br />
#pool 2.debian.pool.ntp.org iburst<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
<br />
# systemctl restart chronyd.service<br />
Cheque com o '''chronyc''' se os servidores estão OK:<br />
# chronyc sourcestats<br />
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev<br />
==============================================================================<br />
a.st1.ntp.br 10 5 155m -0.027 0.030 -71us 51us<br />
b.st1.ntp.br 11 7 344m +0.068 0.079 +23ms 382us<br />
c.st1.ntp.br 6 3 344m +0.026 0.037 -124us 92us<br />
200.20.186.76 9 3 138m -0.022 0.031 +172us 42us<br />
<br />
# chronyc sources<br />
MS Name/IP address Stratum Poll Reach LastRx Last sample<br />
===============================================================================<br />
^* a.st1.ntp.br 1 10 377 588 +487us[ +397us] +/- 12ms<br />
^- b.st1.ntp.br 2 10 377 830 +23ms[ +23ms] +/- 49ms<br />
^+ c.st1.ntp.br 2 10 21 1038 -147us[ -242us] +/- 17ms<br />
^+ 200.20.186.76 1 10 377 1032 +381us[ +285us] +/- 15ms<br />
<br />
== Configurando o FRRouting ==<br />
Nesse ponto iremos configurar o '''FRRouting''' para enviar os IPs das '''loopbacks''' e o '''/30''' para o nosso PE do diagrama. Em '''/etc/frr/daemons''' habilite o parâmetro conforme abaixo:<br />
ospfd=yes<br />
Edite o arquivo '''/etc/frr/frr.conf''' e deixe com o conteúdo abaixo, para ficar conforme nosso diagrama do projeto. Apenas troque '''<SENHA>''' por uma senha para fechar o OSPF com mais segurança. Essa senha deve ser usada dos dois lados.<br />
frr version 8.2.2<br />
frr defaults traditional<br />
hostname dns-recursivo-01<br />
log syslog informational<br />
no ip forwarding<br />
no ipv6 forwarding<br />
service integrated-vtysh-config<br />
!<br />
interface ens18<br />
ip ospf message-digest-key 5 md5 '''<SENHA>'''<br />
ip ospf network point-to-point<br />
exit<br />
!<br />
router ospf<br />
ospf router-id 172.16.0.6<br />
network 10.10.10.10/32 area 0.0.0.0<br />
network 10.10.9.9/32 area 0.0.0.0<br />
network 172.16.0.4/30 area 0.0.0.0<br />
area 0 authentication message-digest<br />
exit<br />
!<br />
<br />
# systemctl restart frr.service<br />
Cheque se está tudo OK com o OSPF e verifique no PE se está recebendo os prefixos anunciados.<br />
# vtysh -c 'show ip ospf neighbor'<br />
<br />
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL<br />
172.16.0.5 1 Full/- 10m49s 35.310s 172.16.0.5 ens18:172.16.0.6 0 0 0<br />
<br />
# vtysh -c 'show ip ospf neighbor detail'<br />
<br />
Neighbor 172.16.0.5, interface address 172.16.0.5<br />
In the area 0.0.0.0 via interface ens18<br />
Neighbor priority is 1, State is Full/-, 5 state changes<br />
Most recent state change statistics:<br />
Progressive change 21w3d15h ago<br />
DR is 0.0.0.0, BDR is 0.0.0.0<br />
Options 18 *|-|-|EA|-|-|E|-<br />
Dead timer due in 34.685s<br />
Database Summary List 0<br />
Link State Request List 0<br />
Link State Retransmission List 0<br />
Thread Inactivity Timer on<br />
Thread Database Description Retransmision off<br />
Thread Link State Request Retransmission on<br />
Thread Link State Update Retransmission on<br />
<br />
Graceful restart Helper info:<br />
Graceful Restart HELPER Status : None<br />
<br />
== Configurando o Unbound ==<br />
Abaixo a configuração que usaremos nos servidores atentando para o detalhe do '''num-threads''', esse deve ter o valor igual ao número de CPUs do servidor.<br />
<br />
Também os IPs utilizados em '''outgoing-interface''' que serão diferentes em cada servidor, esses serão os IPs usados para '''recursividade'''. Consulte o manual do Unbound para obter mais informações sobre cada parâmetro listado na configuração.<br />
<br />
O tuning no Unbound pode ser alterado conforme abaixo:<br />
num-threads = nº CPUs<br />
so-reuseport = yes<br />
*-slabs = potência de 2 próximo ao num-threads<br />
msg-cache-size = 1g (quantidade de memória pra usar de cache)<br />
rrset-cache-size = 2 * msg-cache-size<br />
outgoing-range = 8192<br />
num-queries-per-thread = 4096<br />
so-rcvbuf e so-sndbuf = 4m ou 8m para servidores com muita requisição<br />
Agora vamos criar nosso arquivo de configuração base em '''/etc/unbound/unbound.conf.d/local.conf''':<br />
server:<br />
verbosity: 1<br />
statistics-interval: 0<br />
statistics-cumulative: no<br />
extended-statistics: yes<br />
num-threads: 4<br />
serve-expired: yes<br />
interface: 127.0.0.1<br />
interface: 10.10.10.10<br />
interface: 10.10.9.9<br />
interface: 172.16.0.6<br />
interface: ::1<br />
interface-automatic: no<br />
outgoing-interface: 198.18.1.10<br />
outgoing-interface: 2001:db8::faca:198:18:1:10<br />
outgoing-range: 8192<br />
outgoing-num-tcp: 1024<br />
incoming-num-tcp: 2048<br />
so-rcvbuf: 4m<br />
so-sndbuf: 4m<br />
so-reuseport: yes<br />
edns-buffer-size: 1232<br />
msg-cache-size: 1g<br />
msg-cache-slabs: 4<br />
num-queries-per-thread: 4096<br />
rrset-cache-size: 2g<br />
rrset-cache-slabs: 4<br />
infra-cache-slabs: 4<br />
do-ip4: yes<br />
do-ip6: yes<br />
do-udp: yes<br />
do-tcp: yes<br />
chroot: ""<br />
username: "unbound"<br />
directory: "/etc/unbound"<br />
logfile: "/var/log/unbound/unbound.log"<br />
use-syslog: no<br />
log-time-ascii: yes<br />
log-queries: no<br />
pidfile: "/var/run/unbound.pid"<br />
root-hints: "/usr/share/dns/root.hints"<br />
hide-identity: yes<br />
hide-version: yes<br />
unwanted-reply-threshold: 10000000<br />
prefetch: yes<br />
prefetch-key: yes<br />
rrset-roundrobin: yes<br />
minimal-responses: yes<br />
module-config: "respip validator iterator"<br />
val-clean-additional: yes<br />
val-log-level: 1<br />
key-cache-slabs: 4<br />
deny-any: yes<br />
access-control: 198.18.0.0/22 allow<br />
access-control: 2001:db8::/32 allow<br />
<br />
rpz:<br />
name: rpz.block.host.local.zone<br />
zonefile: /etc/unbound/rpz.block.hosts.zone<br />
rpz-action-override: nxdomain<br />
<br />
python:<br />
<br />
auth-zone:<br />
name: "."<br />
master: "b.root-servers.net"<br />
master: "c.root-servers.net"<br />
master: "d.root-servers.net"<br />
master: "f.root-servers.net"<br />
master: "g.root-servers.net"<br />
master: "k.root-servers.net"<br />
master: "lax.xfr.dns.icann.org"<br />
master: "iad.xfr.dns.icann.org"<br />
fallback-enabled: yes<br />
for-downstream: no<br />
for-upstream: yes<br />
zonefile: "/var/lib/unbound/root.zone"<br />
No parâmetro '''interface''' colocamos os IPs que serão usados para consulta dos clientes como o '''10.10.10.10''' e o '''10.10.9.9'''. Ali repare que coloquei também o IP privado '''172.16.0.6''', isso porque cada servidor terá o seu IP privado e este deve ser usado pelo seu sistema de monitoramento para checar cada servidor. No '''outgoing-interface''' teremos os IPs, tanto '''IPv4''' quanto '''IPv6''', para que seja feita a recursividade na Internet utilizando eles. Não tem '''IPv6''' ainda na sua rede? Dê uma olhada nesse artigo. Outro parâmetro importante é o '''access-control''' e é através dele que liberamos os prefixos IP para consultarem no nosso DNS Recursivo. No exemplo estou liberando todo o prefixo '''198.18.0.0/22''' e o prefixo '''2001:db8::/32'''. Além da ACL no Unbound, recomendo que crie um filtro de pacotes com iptables ou nftables protegendo seu sistema e liberando as portas '''53/UDP''', '''53/TCP''' e '''443/TCP''' apenas para seus clientes. Falarei sobre a '''443/TCP''' mais para frente nessa mesma documentação.<br />
<br />
Agora criaremos o arquivo '''RPZ''' ('''Response Policy Zones'''). Esse arquivo contém os sites que serão bloqueados via '''<abbr>DNS</abbr> Recursivo'''. São aqueles sites que às vezes você recebe um Ofício da Justiça solicitando o bloqueio deles. Não entrarei no mérito da efetividade desses bloqueios, porque muitos de vocês sabem que tecnicamente, existem formas de se fazer um bypass através desses bloqueios. Contudo vamos deixar nosso ambiente preparado para esses bloqueios e por isso crie o arquivo '''/etc/unbound/rpz.block.hosts.zone''' com esse conteúdo de exemplo:<br />
$TTL 2h<br />
@ IN SOA localhost. root.localhost. (2 6h 1h 1w 2h)<br />
IN NS localhost.<br />
; RPZ manual block hosts<br />
*.josedascoves.com CNAME .<br />
josedascoves.com CNAME .<br />
No exemplo acima estamos bloqueando qualquer consulta de DNS para '''josedascoves.com''' ou qualquer coisa '''.josedascoves.com'''.<br />
<br />
Para testar podemos fazer assim do próprio servidor:<br />
# host josedascoves.com ::1<br />
Using domain server:<br />
Name: ::1<br />
Address: ::1#53<br />
Aliases:<br />
<br />
Host josedascoves.com not found: 3(NXDOMAIN)<br />
Se a resposta for '''NXDOMAIN''' então está funcionando o bloqueio. Para incluir novos bloqueios basta adicionar os domínios, um abaixo do outro, conforme o exemplo que coloquei no arquivo RPZ.<br />
<br />
== Acertando o resolv.conf ==<br />
Vamos modificar nosso /etc/resolv.conf para utilizar DNS externo. Sim você deve estar se perguntando em qual situação isso seria utilizado. Primeiro entenda que o Unbound não irá utilizar o DNS externo para fazer as consultas na Internet e sim, qualquer teste que você faça do servidor precisará apontar para o Unbound usando os IPs '''127.0.0.1''' ou '''::1'''. Faremos isso pela seguinte situação: imagine que o daemon unbound morreu mas você ainda continua com conectividade na Internet. Você conseguiria acessar qualquer local na Internet através do IP mas não através do hostname porque não conseguiria resolver nomes, seu unbound estaria fora do ar. Imagine ainda que você gostaria que seu servidor te avisasse do problema via Telegram ou e-mail. Por isso estamos utilizando um DNS externo no '''/etc/resolv.conf''', apenas para essas situações. Se você não quiser utilizar desse recurso, pode usar o '''127.0.0.1''' e '''::1''' no lugar.<br />
nameserver 8.8.8.8<br />
nameserver 8.8.4.4<br />
nameserver 2001:4860:4860::8888<br />
<br />
== Script de teste de recursividade ==<br />
Estamos montando uma '''Rede de DNS Recursivo Anycast''', então é muito importante que você monitore essa rede para saber se algum node morreu e iniciar o troubleshooting, resolver o problema e levantar o sistema novamente. Tudo isso é importante mas o cliente não deve ficar esperando até você resolver o problema, seu sistema precisa ser inteligente o suficiente para se remover da Rede quando tiver um problema e se inserir novamente, quando o problema estiver sido solucionado. Se você montar uma Rede de DNS e um dos nodes apresentar algum problema, todos os clientes atendidos por aquele node migrarão automaticamente e transparentemente para outro '''DNS Recursivo Anycast''' mais próximo. Isso se chama '''disponibilidade'''.<br />
<br />
O script '''/root/scripts/checa_dns.sh''' abaixo tem a função de fazer os testes de recursividade e checar se o daemon do unbound continua rodando. Se algo acontecer, ele para o anúncio do '''10.10.10.10''' e '''10.10.9.9''' e retorna eles quando tudo estiver resolvido.<br />
# mkdir /root/scripts<br />
<br />
#!/usr/bin/env bash<br />
#Script para teste de recursividade v2.2<br />
# Por Marcelo Gondim<br />
# Em 24-12-2022<br />
#<br />
# checa_dns.sh is free software; you can redistribute it and/or modify<br />
# it under the terms of the GNU General Public License as published by<br />
# the Free Software Foundation; either version 2 of the License, or<br />
# (at your option) any later version.<br />
#<br />
# This program is distributed in the hope that it will be useful,<br />
# but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
# GNU General Public License for more details.<br />
#<br />
# You should have received a copy of the GNU General Public License<br />
# along with this program; if not, write to the Free Software<br />
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
#-----------------------------------------------------------------------<br />
#Informe um domínio por linha:<br />
dominios_testar=(<br />
www.google.com<br />
www.terra.com.br<br />
www.uol.com.br<br />
www.globo.com<br />
www.facebook.com<br />
www.youtube.com<br />
www.twitch.com<br />
www.discord.com<br />
www.debian.org<br />
www.redhat.com<br />
)<br />
corte_taxa_falha=100 #Porcentagem de falha para executar uma ação<br />
#-----------------------------------------------------------------------<br />
remove_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" != "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME morreu!" | /usr/local/sbin/telegram-notify --error --text -<br />
fi<br />
}<br />
<br />
adiciona_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" == "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME retornou do inferno!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
}<br />
<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
#echo "Servidor $HOSTNAME morreu DNS mas tentando levantar!" | /usr/local/sbin/telegram-notify --error --text -<br />
systemctl restart unbound<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
#echo "Servidor $HOSTNAME servico DNS voltou mas tinha morrido!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
<br />
qt_falhas=0<br />
qt_total="${#dominios_testar[@]}"<br />
echo "total_dominios: $qt_total"<br />
for site in "${dominios_testar[@]}"<br />
do<br />
resolver="127.0.0.1"<br />
echo -e " - dominio $site - $resolver - \c"<br />
host $site $resolver &> /dev/null<br />
if [ $? -ne 0 ]; then<br />
((qt_falhas++))<br />
echo -e "[Falhou]"<br />
else<br />
echo -e "[OK]"<br />
fi<br />
done<br />
<br />
taxa_falha=$((qt_falhas*100/qt_total))<br />
echo "Falhas $qt_falhas/$qt_total ($taxa_falha%)"<br />
<br />
if [ "$taxa_falha" -ge "$corte_taxa_falha" ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
adiciona_ospf<br />
Se rodarmos o script manualmente veremos isto:<br />
# /root/scripts/checa_dns.sh<br />
total_dominios: 10<br />
- dominio www.google.com - 127.0.0.1 - [OK]<br />
- dominio www.terra.com.br - 127.0.0.1 - [OK]<br />
- dominio www.uol.com.br - 127.0.0.1 - [OK]<br />
- dominio www.globo.com - 127.0.0.1 - [OK]<br />
- dominio www.facebook.com - 127.0.0.1 - [OK]<br />
- dominio www.youtube.com - 127.0.0.1 - [OK]<br />
- dominio www.twitch.com - 127.0.0.1 - [OK]<br />
- dominio www.discord.com - 127.0.0.1 - [OK]<br />
- dominio www.debian.org - 127.0.0.1 - [OK]<br />
- dominio www.redhat.com - 127.0.0.1 - [OK]<br />
Falhas 0/10 (0%)<br />
Se acontecer 100% de falhas o script irá remover os anúncios do OSPF. Se o daemon do unbound morrer, ele tentará reiniciá-lo. Se tudo normalizar o script irá retornar os anúncios para o OSPF. Deixei comentado no script as partes que enviariam uma notificação para o Telegram. Existem diversas documentações sobre isso na Internet, eu mesmo tenho uma. Assim que eu publicar aqui, atualizo essa documentação e sinta-se à vontade de modificar como desejar.<br />
# chmod 700 /root/scripts/checa_dns.sh<br />
Adicione a linha abaixo em seu '''/etc/crontab''':<br />
*/1 * * * * root /root/scripts/checa_dns.sh<br />
<br />
== Habilitando o DoH (<abbr>DNS</abbr> over HTTPS) - opcional ==<br />
Para habilitar o '''DoH''' no Unbound é bem simples mas só é possível com a versão '''1.17.1''' que vem no '''bullseye-backports''' e que virá na próxima versão do '''Debian 12 (Bookworm)'''. O recurso do '''DoH''' vem para trazer mais segurança e privacidade para o usuário. É um recurso muito pouco utilizado ainda mas que seu cliente pode vir a pedir algum dia.<br />
<br />
Você precisará gerar certificados SSL legítimos e para isso você poderá usar o '''Let's Encrypt''' só que de uma forma não tão convencional.<br />
<br />
Na sequência vamos instalar o Let's Encrypt para gerarmos nosso certificado SSL:<br />
# apt install letsencrypt<br />
Escolha um '''hostname''' para ser usado no nosso '''DoH''' e aponte ele no seu DNS Autoritativo para seus IPs 10.10.10.10 e 10.10.9.9. Aqui vamos usar o seguinte como exemplo: '''doh.brasilpeeringforum.org'''. Para gerarmos nosso certificado iremos usar o tipo '''DNS-01''', ele não necessita que tenhamos um servidor web rodando no servidor e nem tão pouco levanta um serviço na porta 80 para checar o hostname. Ele utiliza o DNS como validador e vai te solicitar que crie um registro '''CNAME''' no seu '''DNS Autoritativo''' para provar que você tem o controle sobre aquele hostname. Antes disso vamos instalar um programa em Python para podermos automatizar nossa renovação de certificado no futuro. Esse programa se encontra '''[https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py aqui]''' mas vou deixá-lo abaixo já modificado o interpretador.<br />
<br />
Crie o arquivo '''/etc/letsencrypt/acme-dns-auth.py''' com o conteúdo abaixo:<br />
#!/usr/bin/env python3<br />
import json<br />
import os<br />
import requests<br />
import sys<br />
<br />
### EDIT THESE: Configuration values ###<br />
<br />
# URL to acme-dns instance<br />
ACMEDNS_URL = "<nowiki>https://auth.acme-dns.io</nowiki>"<br />
# Path for acme-dns credential storage<br />
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"<br />
# Whitelist for address ranges to allow the updates from<br />
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]<br />
ALLOW_FROM = []<br />
# Force re-registration. Overwrites the already existing acme-dns accounts.<br />
FORCE_REGISTER = False<br />
<br />
### DO NOT EDIT BELOW THIS POINT ###<br />
### HERE BE DRAGONS ###<br />
<br />
DOMAIN = os.environ["CERTBOT_DOMAIN"]<br />
if DOMAIN.startswith("*."):<br />
DOMAIN = DOMAIN[2:]<br />
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN<br />
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]<br />
<br />
<br />
class AcmeDnsClient(object):<br />
"""<br />
Handles the communication with ACME-DNS API<br />
"""<br />
<br />
def __init__(self, acmedns_url):<br />
self.acmedns_url = acmedns_url<br />
<br />
def register_account(self, allowfrom):<br />
"""Registers a new ACME-DNS account"""<br />
<br />
if allowfrom:<br />
# Include whitelisted networks to the registration call<br />
reg_data = {"allowfrom": allowfrom}<br />
res = requests.post(self.acmedns_url+"/register",<br />
data=json.dumps(reg_data))<br />
else:<br />
res = requests.post(self.acmedns_url+"/register")<br />
if res.status_code == 201:<br />
# The request was successful<br />
return res.json()<br />
else:<br />
# Encountered an error<br />
msg = ("Encountered an error while trying to register a new acme-dns "<br />
"account. HTTP status {}, Response body: {}")<br />
print(msg.format(res.status_code, res.text))<br />
sys.exit(1)<br />
<br />
def update_txt_record(self, account, txt):<br />
"""Updates the TXT challenge record to ACME-DNS subdomain."""<br />
update = {"subdomain": account['subdomain'], "txt": txt}<br />
headers = {"X-Api-User": account['username'],<br />
"X-Api-Key": account['password'],<br />
"Content-Type": "application/json"}<br />
res = requests.post(self.acmedns_url+"/update",<br />
headers=headers,<br />
data=json.dumps(update))<br />
if res.status_code == 200:<br />
# Successful update<br />
return<br />
else:<br />
msg = ("Encountered an error while trying to update TXT record in "<br />
"acme-dns. \n"<br />
"------- Request headers:\n{}\n"<br />
"------- Request body:\n{}\n"<br />
"------- Response HTTP status: {}\n"<br />
"------- Response body: {}")<br />
s_headers = json.dumps(headers, indent=2, sort_keys=True)<br />
s_update = json.dumps(update, indent=2, sort_keys=True)<br />
s_body = json.dumps(res.json(), indent=2, sort_keys=True)<br />
print(msg.format(s_headers, s_update, res.status_code, s_body))<br />
sys.exit(1)<br />
<br />
class Storage(object):<br />
def __init__(self, storagepath):<br />
self.storagepath = storagepath<br />
self._data = self.load()<br />
<br />
def load(self):<br />
"""Reads the storage content from the disk to a dict structure"""<br />
data = dict()<br />
filedata = ""<br />
try:<br />
with open(self.storagepath, 'r') as fh:<br />
filedata = fh.read()<br />
except IOError as e:<br />
if os.path.isfile(self.storagepath):<br />
# Only error out if file exists, but cannot be read<br />
print("ERROR: Storage file exists but cannot be read")<br />
sys.exit(1)<br />
try:<br />
data = json.loads(filedata)<br />
except ValueError:<br />
if len(filedata) > 0:<br />
# Storage file is corrupted<br />
print("ERROR: Storage JSON is corrupted")<br />
sys.exit(1)<br />
return data<br />
<br />
def save(self):<br />
"""Saves the storage content to disk"""<br />
serialized = json.dumps(self._data)<br />
try:<br />
with os.fdopen(os.open(self.storagepath,<br />
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:<br />
fh.truncate()<br />
fh.write(serialized)<br />
except IOError as e:<br />
print("ERROR: Could not write storage file.")<br />
sys.exit(1)<br />
<br />
def put(self, key, value):<br />
"""Puts the configuration value to storage and sanitize it"""<br />
# If wildcard domain, remove the wildcard part as this will use the<br />
# same validation record name as the base domain<br />
if key.startswith("*."):<br />
key = key[2:]<br />
self._data[key] = value<br />
<br />
def fetch(self, key):<br />
"""Gets configuration value from storage"""<br />
try:<br />
return self._data[key]<br />
except KeyError:<br />
return None<br />
<br />
if __name__ == "__main__":<br />
# Init<br />
client = AcmeDnsClient(ACMEDNS_URL)<br />
storage = Storage(STORAGE_PATH)<br />
<br />
# Check if an account already exists in storage<br />
account = storage.fetch(DOMAIN)<br />
if FORCE_REGISTER or not account:<br />
# Create and save the new account<br />
account = client.register_account(ALLOW_FROM)<br />
storage.put(DOMAIN, account)<br />
storage.save()<br />
<br />
# Display the notification for the user to update the main zone<br />
msg = "Please add the following CNAME record to your main DNS zone:\n{}"<br />
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])<br />
print(msg.format(cname))<br />
<br />
# Update the TXT record in acme-dns instance<br />
client.update_txt_record(account, VALIDATION_TOKEN)<br />
<br />
# chmod +x /etc/letsencrypt/acme-dns-auth.py<br />
Usaremos a seguinte instrução para criar nosso certificado:<br />
# certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d doh.brasilpeeringforum.org<br />
Saving debug log to /var/log/letsencrypt/letsencrypt.log<br />
Plugins selected: Authenticator manual, Installer None<br />
Cert is due for renewal, auto-renewing...<br />
Renewing an existing certificate for doh.brasilpeeringforum.org<br />
Performing the following challenges:<br />
dns-01 challenge for doh.brasilpeeringforum.org<br />
Running manual-auth-hook command: /etc/letsencrypt/acme-dns-auth.py<br />
Output from manual-auth-hook command acme-dns-auth.py:<br />
Please add the following CNAME record to your main DNS zone:<br />
_acme-challenge.doh.brasilpeeringforum.org CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
<br />
Waiting for verification...<br />
<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Challenges loaded. Press continue to submit to CA. Pass "-v" for more info about<br />
challenges.<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Press Enter to Continue<br />
Nesse momento você cria o registro '''CNAME''' no seu DNS Autoritativo conforme ele solicitou: '''_acme-challenge.doh.brasilpeeringforum.org IN CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.''' e somente depois de criado e checado no DNS, você pressiona o '''Enter''' para continuar. Você pode checar dessa forma:<br />
# host -t cname _acme-challenge.doh.brasilpeeringforum.org<br />
_acme-challenge.doh.brasilpeeringforum.org is an alias for b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
Para que nosso certificado seja automaticamente renovado colocaremos no '''/etc/crontab''' a seguinte linha abaixo:<br />
00 00 1 * * root /usr/bin/certbot -q renew<br />
Acima temos a instrução para renovação automática do certificado. Repare que você vai precisar também copiar esse certificado para seus outros servidores, escolha um servidor para manter o certificado sempre atualizado e crie um script que faça a mesma cópia remotamente para os outros servidores. O '''scp''' e o '''rsync''' são seus aliados nisso.<br />
<br />
=== Configurando o Unbound ===<br />
Em nosso '''/etc/unbound/unbound.conf.d/local.conf''', adicionaremos no bloco "'''server:'''" o seguinte:<br />
interface: 10.10.10.10@443<br />
interface: 10.10.9.9@443<br />
tls-service-key: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/privkey.pem"<br />
tls-service-pem: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/fullchain.pem"<br />
Para usar o recurso do '''DoH''' você precisará habilitar o recurso no seu navegador e informar a URL. Vou colocar o exemplo do '''Google Chrome''': Digite '''chrome://settings/security?search=dns''' no seu Chrome e ative '''Usar DNS seguro''', selecione '''Personalizado''' e adicione nossa URL:<br />
[[Arquivo:Doh bpf2.png|nenhum|commoldura]]<br />
<br />
== Finalizando ==<br />
Aqui finalizamos nosso projeto para uma Rede de DNS(s) Recursivos Anycast com Hyperlocal. Esse projeto é escalável, seguro, resiliente e você entregará muito mais qualidade de Internet para o seu cliente. Pare de entregar o '''8.8.8.8''' para os seus clientes, você está contribuindo para uma Internet mais lenta, sem a qualidade que o seu cliente merece. Investi meu tempo, que é muito pouco, para deixar esse documento para a comunidade, para você melhorar o seu ISP, para dar um UP! nele, então vamos começar 2023 com o pé direito. O que acha?<br />
<br />
Como prova de conceito, uma imagem abaixo onde temos uma Rede em produção de DNS(s) Recursivos Anycast e apontando exatamente o momento em que houve alguma situação que fez com que as queries de DNS, convergissem de um node para outro, de forma transparente e automática para o cliente. Podemos notar também que ao ser resolvido o problema, o tráfego retornou para o seu node correto:<br />
[[Arquivo:Convergencia.png|nenhum|commoldura]]<br />
<br />
== KINDNS (Stands for Knowledge-Sharing and Instantiating Norms for DNS and Naming Security) ==<br />
Achou que havia terminado? Agora que você tem a capacidade de montar uma '''Rede de DNS Recursivo''' com todas essas features acima, com todas as ferramentas que foram comentadas, o que acha de certificar o que fez?<br />
<br />
Assim como o [https://www.manrs.org/ MANRS] veio para certificar nosso sistema de roteamento na Internet, agora temos o [https://kindns.org/ KINDNS] para certificar que nossos sistemas de DNS estão bem feitos e dentro dos padrões de segurança. Existem '''7 ações''' que podem ser certificadas para nossos DNS Recursivos e estão aqui em https://kindns.org/shared-private-resolvers/. Com essa nossa documentação, se bem aplicada, você pode se candidatar ao KINDNS e ter seu ASN listado aqui https://kindns.org/participants/<br />
<br />
Obter e manter o '''MANRS''' e '''KINDNS''' demonstra seu compromisso com as Boas Práticas, contribui para termos uma '''Internet''' mais segura e te abre portas para novos negócios que possam exigir essas conformidades.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]<br />
__FORCARTDC__</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=DNS_Recursivo_Anycast_Hyperlocal&diff=3665DNS Recursivo Anycast Hyperlocal2023-12-30T17:32:15Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
==Introdução==<br />
Você sabe como funciona a Internet? Essa é uma pergunta que meu amigo '''Thiago Ayub''' sempre faz aos seus candidatos à vagas de emprego e não importa o quanto tenham de experiência em '''Engenharia de Redes''', todos sempre travam nesse momento. Todos estão sempre prontos e preparados para resolver os problemas mais cabeludos em '''BGP''', '''OSPF''', '''MPLS''', etc mas travam com essa simples pergunta. Para contextualizar e visualizarmos melhor vamos nos atentar à imagem abaixo e uma explicação simplificada de como funciona:<br />
[[Arquivo:Dns hierarquia.png|esquerda|commoldura]]<br />
Tudo começa com um usuário sentado confortavelmente e querendo acessar um conteúdo disponível na Internet. Ele digita em seu navegador preferido a URL: '''<nowiki>https://wiki.brasilpeeringforum.org</nowiki>''',<br />
<br />
'''<big>1)</big>''' <big>O</big> <big>navegador irá requisitar do '''DNS Recursivo''' utilizado pelo usuário, o '''endereço IP''' que responde pelo nome '''wiki.'''</big>'''brasilpeeringforum.org'''<big>. Isso porque todos os acessos se dão na Internet através do '''endereço''' '''IP''' e não através do '''nome'''. Imaginem se tivéssemos que decorar os endereços IPs de todos os sites e serviços que quiséssemos acessar na Internet?</big> <br />
<br />
<big>'''2)''' Nosso DNS Recursivo checa se a informação consta em seu cache.</big> Se a informação existir ela é devolvida ao navegador do usuário e aí este consegue acessar o site.<br />
<br />
'''3)''' Do contrário o DNS Recursivo pergunta ao '''Root Server''' quem é o '''TLD (Top Level Domain)''' responsável para atender a requisição. <br />
<br />
'''4)''' O '''Root Server''' informa ao DNS Recursivo o endereço do '''TLD responsável'''. No Brasil o '''TLD''' responsável pelo '''.br''' seria o '''Registro.br'''.<br />
<br />
'''5)''' O DNS Recursivo pergunta ao '''TLD''' sobre '''wiki.brasilpeeringforum.org''' e este responde com os endereços IP dos '''DNS Autoritativos''' responsáveis pelo domínio '''brasilpeeringforum.org.'''<br />
<br />
'''6)''' O DNS Recursivo pergunta aos '''DNS Autoritativos''' pelo '''wiki.brasilpeeringforum.org''' e este responde com o '''endereço IP'''.<br />
<br />
'''7)''' Por último o DNS Recursivo devolve a informação para o navegador do usuário.<br />
<br />
Como que se dá a comunicação entre os '''DNS(s) Recursivos, Root Servers, TLDs''' e '''Autoritativos'''? Como que o navegador do usuário, após receber o IP do site, consegue chegar no servidor que tem o conteúdo? Isso só é possível devido ao protocolo chamado '''BGP (Border Gateway Protocol)''', todos os caminhos que conhecemos como rotas de destino, são anunciadas por milhares de participantes na '''Internet''' conhecidos como '''AS (Autonomous System)''', esses participantes se interligam para disponibilizar conteúdos e acessos pelo mundo aos milhares de usuários. É uma imensa rede colaborativa formada por Empresas, Universidades, Governos e todos que queiram se interconectar. Percebam que sem o '''BGP''', que serve de caminho para chegarmos nos conteúdos e sem o '''DNS (Domain Name System)''' para traduzir o nome para o endereço IP, a '''Internet''' não funcionaria e por isso precisamos cuidar muito bem desses dois serviços.<br />
<br><br><br><br><br />
Mas não acaba por aí. O '''DNS Recursivo''' tem um papel muito importante para o Provedor de Internet e que envolve segurança, qualidade de acesso à Internet e a disponibilidade do serviço entregue ao cliente. Quando bem configurado acelera as consultas dos acessos graças ao seu cache interno, mas para que isso seja percebido pelo assinante, é necessário que esteja o mais próximo possível do seu cliente.<br />
<br><br />
== Um erro que destrói a qualidade do nosso serviço ==<br />
Um erro muito comum que muitas operadoras cometem é utilizar DNS Recursivo externo, como o '''8.8.8.8''', '''1.1.1.1''' e outros, para seus clientes. Quanto mais próximo dos seus clientes, mais qualidade de serviço estará entregando a eles. Conteúdos serão entregues mais rapidamente pois serão resolvidos e armazenados em caches locais e não consultados remotamente na Internet. Para falar mais sobre isso, te convido leitor desse documento, que assista essa palestra do '''Thiago Ayub''' no '''GTER 51/GTS 37''' (2022) '''8.888 MOTIVOS PARA NÃO USAR DNS RECURSIVO EXTERNO EM SEU AS''': https://www.youtube.com/watch?v=Rsvpu5uF2Io<br />
<br />
== Objetivo ==<br />
O objetivo desta documentação não é te ensinar tudo sobre '''DNS''', '''BGP''', '''OSPF''' e nem tão pouco sobre GNU/Linux e sim te mostrar um exemplo de servidor DNS Recursivo implementado pensando em segurança, qualidade e resiliência. Usaremos em todas as nossas documentações o [https://www.debian.org/ Debian GNU/Linux], por ser uma distribuição que considero uma obra de arte criada por uma enorme comunidade séria, com vasta experiência de anos, qualidade no empacotamento dos programas, estável e com uma equipe de segurança excelente e ativa. Caso você leitor, utilize alguma outra distribuição GNU/Linux, todo conteúdo apresentado aqui pode ser aplicado em outras distros, desde que respeitando as particularidades de cada uma.<br />
<br />
Aqui construiremos um sistema do tipo '''Anycast''', ou seja, terás o serviço rodando em diversas localidades da sua Rede utilizando o mesmo endereçamento IP e que atenderá seu cliente mais próximo. Em caso de falhas, seus clientes automaticamente e de forma transparente continuarão consultando o DNS mais próximo deles. Para que ele funcione dessa forma você precisará ter uma '''Rede OSPF''' implementada no seu Provedor Internet ou algum outro protocolo como por exemplo o '''ISIS,''' mas esse documento não irá abordar o '''ISIS'''. Também utilizaremos o '''Hyperlocal''' como recurso adicional para gerar algumas proteções de segurança e velocidade na resposta relacionada aos servidores de DNS Raiz da Internet.<br />
<br />
== Diagrama ==<br />
Para exemplificar nosso servidor de DNS Recursivo, usaremos como base das explicações um diagrama demonstrando o uso do DNS Recursivo em uma Rede fictícia. Adotaremos IPs privados e reservados para demonstrar todo o ambiente do Provedor de Internet.<br />
[[Arquivo:Diagrama dns recursivo.drawio.png|esquerda|commoldura]]<br />
Nesse diagrama podemos observar alguns detalhes técnicos como por exemplo: existem '''3 servidores de DNS Recursivo''' posicionados em locais diferentes, que poderiam estar em bairros diferentes e até em cidades diferentes. Em cada servidor teremos '''2 loopbacks''' com os IPs:<br />
<br />
'''10.10.10.10/32'''<br />
<br />
'''10.10.9.9/32'''<br />
<br />
Esses IPs serão entregues pelos concentradores '''PPPoE''' ou '''IPoE''' ('''BNG''') para seus clientes como '''DNS primário''' e '''secundário'''. Podemos usar IPs privados como DNS primário e secundário em um ambiente real? Sim podemos, desde que não sejam IPs que possam ter problemas com as redes privadas dos clientes. Ex.: rede do cliente usando '''192.168.0.0/24'''. Se entregarmos o DNS sendo '''192.168.0.10''' e '''192.168.0.20''' teremos problemas e o cliente ficará sem Internet, porque '''192.168.0.10''' e '''192.168.0.20''' fazem parte da rede '''192.168.0.0/24'''.<br />
<br />
Agora entregando '''10.10.10.10''' e '''10.10.9.9''' não teríamos problemas com a rede '''192.168.0.0/24'''.<br />
<br />
'''Motivos para usarmos IPs privados:'''<br />
* O principal motivo está relacionado com a segurança, uma vez que sendo um IP privado, não pode sofrer ataques DDoS direcionados diretamente para ele, vindos da Internet.<br />
* Nem mesmo o cliente da sua rede conhece os '''IPs públicos''' utilizados para recursividade na Internet.<br />
* Memorizar os IPs '''10.10.10.10''' e '''10.10.9.9''' é tão fácil quanto memorizar o '''8.8.8.8''' e o '''1.1.1.1'''. Mais fácil para o seu técnico guardar essa informação e utilizar onde for necessário.<br />
Cada servidor DNS Recursivo possui um '''IPv4 público''', aqui representado por '''198.18.x.x/27''' e um '''IPv6 global''' representado por um IP dentro do prefixo '''2001:db8::/32'''. Cada servidor precisa ter os seus próprios IPs e são através destes IPs que as consultas de DNS serão realizadas na Internet.<br />
<br />
Nessa topologia usando '''Anycast''', o cliente será sempre atendido pelo '''DNS Recursivo''' mais próximo, desde que os pesos no '''OSPF''' estejam ajustados corretamente.<br />
<br><br><br><br><br><br><br />
== Dados do servidor ==<br />
Podemos utilizar um sistema virtualizado ou não. Sistemas virtualizados são bem vindos pois são mais simples quando precisamos fazer backups, levantar outros sistemas sem complicações e se precisarmos restaurar rapidamente algum sistema que ficou indisponível por algum motivo. A configuração abaixo tem capacidade para atender algo em torno a '''50.000 assinantes ou mais'''. O DNS Recursivo é um serviço que pode ser utilizado até mesmo em um '''Raspberry Pi''' e atender operações pequenas, nesse caso com o intuito de economizar energia e espaço. Nosso foco aqui é montar uma rede de '''DNS Recursivo Anycast com HyperLocal'''. Como comentei acima o servidor deve ficar o mais próximo dos clientes para termos a '''menor latência possível''' e '''sempre menor que 5ms''' entre o cliente e o servidor.<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Memória<br />
!Disco<br />
!Sistema<br />
|-<br />
|2.4Ghz 6 cores<br />
|16G DDR4<br />
|30G<br />
|Debian 11 amd64 (Bullseye)<br />
|}<br />
<br />
== Softwares utilizados ==<br />
* Debian 11 amd64 (Bullseye) instalação mínima.<br />
<br />
* [https://frrouting.org/ FRRouting].<br />
* Unbound.<br />
* IRQBalance.<br />
* Chrony (NTP/NTS).<br />
* Shell script em bash.<br />
<br />
== Funcionalidades que teremos ==<br />
* Sistema em Anycast.<br />
* Hyperlocal.<br />
* Controle de acesso por <abbr>ACL</abbr>.<br />
* RPZ (Response Policy Zone).<br />
* Bloqueio de consultas do tipo ANY.<br />
* QNAME minimization habilitado. (habilitado por default no Unbound)<br />
* Recursividade em IPv4 e IPv6.<br />
* DNSSEC habilitado.<br />
* <abbr>DoH (DNS</abbr> over HTTPS) habilitado.<br />
<br />
== Monitoramento ==<br />
O monitoramento é algo bem específico e não é o foco deste documento mas é extremamente importante que você monitore seus servidores de DNS por alguma ferramenta como o Zabbix. Aqui mostrarei apenas como enviar as informações para o Zabbix. Algumas coisas que você deveria monitorar nos servidores de DNS Recursivo:<br />
* Serviço do unbound parou.<br />
* Perda de pacotes.<br />
* Latência alta de pacotes.<br />
* Lentidão na resolução de queries.<br />
* CPU alta.<br />
* Load alto.<br />
* Memória com uso alto.<br />
* Disco com pouco espaço.<br />
* Queda brusca nas queries.<br />
* A recursividade parou de funcionar.<br />
* A recursividade voltou a funcionar.<br />
Este abaixo é um exemplo de monitoramento de um sistema de DNS Recursivo que atende 50.000 assinantes:<br />
[[Arquivo:Grafana dns.png|nenhum|commoldura]]<br />
<br />
== Configurando a Rede ==<br />
Nossa documentação será baseada no diagrama apresentado acima e por isso configuraremos apenas um dos três servidores, porque os outros serão configurados da mesma forma, só que com dados diferentes. Para tanto assumirei que já temos um sistema Debian instalado com o mínimo de pacotes e somente com sshd, para que possamos acessar remotamente mais tarde. '''Não instale um ambiente gráfico no servidor''', você não deve querer fazer isso por diversos motivos e os principais: primeiro porque não é um Desktop e segundo porque o ambiente gráfico devoraria toda a memória com recursos que não seriam úteis aqui.<br />
<br />
Em '''/etc/network/interfaces''' deixaremos assim:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto lo:0<br />
iface lo:0 inet static<br />
address 10.10.10.10/32<br />
<br />
auto lo:1<br />
iface lo:1 inet static<br />
address 10.10.9.9/32<br />
<br />
# The primary network interface<br />
auto ens18<br />
iface ens18 inet static<br />
address 198.18.1.10/27<br />
gateway 198.18.1.1<br />
<br />
iface ens18 inet6 static<br />
address 2001:db8::faca:198:18:1:10/64<br />
gateway 2001:db8::faca:198:18:1:1<br />
<br />
# The secondary network interface<br />
auto ens18:0<br />
iface ens18:0 inet static<br />
address 172.16.0.6/30<br />
Nesse cenário temos as duas '''loopbacks''' com os IPs '''10.10.10.10''' e '''10.10.9.9''' que serão anunciados via OSPF para a rede e serem entregues aos clientes via BNG. Os IPs '''198.18.1.10''' e '''2001:db8::faca:198:18:1:10''' serão usados para fazerem a recursividade na Internet tanto em IPv4 quanto em IPv6. Esses IPs não devem ser divulgados para clientes; os IPs públicos são dedicados apenas para essa finalidade.<br />
<br />
== Configuração dos repositórios Debian ==<br />
Deixe o arquivo '''/etc/apt/sources.list''' conforme abaixo:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
Após a configuração vamos instalar alguns pacotes necessários e outros úteis:<br />
# apt update && apt full-upgrade<br />
# apt install net-tools nftables htop iotop sipcalc tcpdump curl gnupg rsync wget host dnsutils mtr-tiny bmon sudo tmux whois ethtool dnstop<br />
<br />
== Fazendo algum tuning no sistema ==<br />
Em '''/etc/sysctl.conf''' adicionamos no final do arquivo essas instruções:<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
Estamos fazendo algumas melhorias de memória, algumas relacionadas a '''conntrack''' porque se for usar um filtro de pacotes stateful, como o '''Netfilter/IPTables''' ou '''Netfilter/NFTables''', o valor default da tabela é pequeno e dependendo da situação, se estourar essa tabela, as consultas de DNS terão problemas também. O DNS Recursivo não deve ficar aberto para qualquer um na Internet, ele deve ser liberado apenas para seus clientes. Podemos fazer através das ACLs do Unbound e pelo filtro de pacotes. O último parâmetro diz respeito ao uso de swap, por padrão o Debian permite o uso de swap após 40% do uso da memória, nesse caso estamos dizendo para o sistema usar o swap com 90% de uso da memória.<br />
<br />
Precisamos adicionar o módulo '''nf_conntrack''' em '''/etc/modules''' para que seja carregado em tempo de boot, senão os parâmetros de '''conntrack''' que colocamos em '''/etc/sysctl.conf''' não serão carregados.<br />
# echo nf_conntrack >> /etc/modules<br />
# modprobe nf_conntrack<br />
# sysctl -p<br />
<br />
== Instalando o FRRouting ==<br />
O FRRouting é o programa que usaremos para fazer os anúncios das nossas loopbacks via OSPF. Nesse documento usaremos a versão 8.x e para isso precisaremos configurar o repositório oficial do FRRouting e instalar os pacotes:<br />
# echo "deb <nowiki>https://deb.frrouting.org/frr</nowiki> bullseye frr-8" > /etc/apt/sources.list.d/frr.list<br />
# curl -s <nowiki>https://deb.frrouting.org/frr/keys.asc</nowiki> | apt-key add -<br />
# apt update<br />
# apt install frr frr-doc frr-pythontools<br />
Aconselho depois de instalar os pacotes, marcá-los para não atualizar juntamente com os demais pacotes, isso é para evitar de ocorrer alguma atualização no FRRouting, que torne o serviço instável por algum motivo. Não que isso vá ocorrer, mas é melhor fazer essa atualização quando realmente for necessário.<br />
# apt-mark hold frr frr-doc frr-pythontools<br />
Após esse comando acima, o sistema manterá a instalação original do pacote intacta. Para desbloquear basta executar o comando abaixo:<br />
# apt-mark unhold frr frr-doc frr-pythontools<br />
<br />
== Removendo o APPARMOR ==<br />
O '''APPARMOR''' às vezes causa mais problemas que solução e se não for fazer uma completa configuração nele, é melhor desabilitá-lo. Para fazer isso efetivamente, o procedimento é esse abaixo:<br />
# mkdir -p /etc/default/grub.d<br />
# echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=0"' | tee /etc/default/grub.d/apparmor.cfg<br />
# update-grub<br />
# reboot<br />
<br />
== Instalando o Unbound ==<br />
Nesse momento ainda não iremos configurar o Unbound, apenas instalar o pacote e acertar o ambiente. Vamos instalar o unbound do backports porque este já possui suporte ao DoH que veremos mais à frente.<br />
# apt -t bullseye-backports install unbound dns-root-data<br />
# mkdir -p /var/log/unbound<br />
# touch /var/log/unbound/unbound.log<br />
# chown -R unbound:unbound /var/log/unbound/<br />
# systemctl restart unbound<br />
Configurando o logrotate:<br />
cat << EOF > /etc/logrotate.d/unbound<br />
/var/log/unbound/unbound.log {<br />
rotate 5<br />
weekly<br />
postrotate<br />
unbound-control log_reopen<br />
endscript<br />
}<br />
EOF<br />
Reiniciando o serviço:<br />
# systemctl restart logrotate.service<br />
<br />
== Preparando o monitoramento do seu DNS Recursivo ==<br />
O monitoramento do seu DNS Recursivo é muito importante e para isso vamos usar um '''template para Zabbix''', que modifiquei juntamente com o seu shell script e que enviará os dados para o seu Zabbix server via '''zabbix-sender'''. O projeto original está aqui '''https://github.com/jeftedelima/Unbound-DNS<nowiki/>.''' O xml alterado está aqui '''https://github.com/gondimcodes/template_zabbix_dns_unbound'''. Embora seja antigo é perfeitamente importável no Zabbix 6.0, por exemplo.<br />
<br />
'''<nowiki/>'''<br />
<br />
Teremos um shell script que você precisará colocar no seu '''/etc/crontab'''. No exemplo abaixo assumi que o shell script está em '''/root/scripts'''. De 5 em 5 minutos os dados serão enviados para o seu Zabbix server.<br />
*/5 * * * * root /root/scripts/unboundSend.sh '''IP_zabbix_server''' '''nome_do_host''' 1> /dev/null<br />
Na linha acima, troque o '''IP_zabbix_server''' pelo '''IP do seu servidor Zabbix''' e o '''nome_do_host''' pelo '''hostname''' '''do seu DNS Recursivo'''. Você precisará instalar o pacote '''zabbix-sender''' no seu DNS Recursivo pois ele será usado para enviar os dados para o Zabbix server.<br />
<br />
Abaixo o '''unboundSend.sh''' também alterado com inclusão de mais dados:<br />
#!/bin/bash<br />
# @Jefte de Lima Ferreira<br />
# jeftedelima at gmail dot com<br />
# CRON Example<br />
# Contributor: Marcelo Gondim - gondim at gmail dot com<br />
# */5 **** root sh /home/dir/unboundSend.sh 192.168.10.1 Unbound 1> /dev/null<br />
<br />
if [ -z ${1} ] || [ -z ${2} ] ; then<br />
echo "You need to specify the IP address of zabbix server and hostname of your DNS Unbound on zabbix"<br />
echo "Usage example: ./unboundSend.sh 192.168.10.1 UnboundServer"<br />
exit 1<br />
fi<br />
<br />
# ZABBIX_SERVER IP<br />
IP_ZABBIX=$1<br />
# NAME Unbound on Zabbix<br />
NAME_HOST=$2<br />
DIR_TEMP=/var/tmp/<br />
FILE="${DIR_TEMP}dump_unbound_control_stats.txt"<br />
unbound-control stats > ${FILE}<br />
<br />
TOTAL_NUM_QUERIES=$(cat ${FILE} | grep -w 'total.num.queries' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEHITS=$(cat ${FILE} | grep -w 'total.num.cachehits' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEMISS=$(cat ${FILE} | grep -w 'total.num.cachemiss' | cut -d '=' -f2)<br />
TOTAL_NUM_PREFETCH=$(cat ${FILE} | grep -w 'total.num.prefetch' | cut -d '=' -f2)<br />
TOTAL_NUM_RECURSIVEREPLIES=$(cat ${FILE} | grep -w 'total.num.recursivereplies' | cut -d '=' -f2)<br />
<br />
TOTAL_REQ_MAX=$(cat ${FILE} | grep -w 'total.requestlist.max' | cut -d '=' -f2)<br />
TOTAL_REQ_AVG=$(cat ${FILE} | grep -w 'total.requestlist.avg' | cut -d '=' -f2)<br />
TOTAL_REQ_OVERWRITTEN=$(cat ${FILE} | grep -w 'total.requestlist.overwritten' | cut -d '=' -f2)<br />
TOTAL_REQ_EXCEEDED=$(cat ${FILE} | grep -w 'total.requestlist.exceeded' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_ALL=$(cat ${FILE} | grep -w 'total.requestlist.current.all' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_USER=$(cat ${FILE} | grep -w 'total.requestlist.current.user' | cut -d '=' -f2)<br />
<br />
TOTAL_TCPUSAGE=$(cat ${FILE} | grep -w 'total.tcpusage' | cut -d '=' -f2)<br />
<br />
NUM_QUERY_TYPE_A=$(cat ${FILE} | grep -w 'num.query.type.A' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NS=$(cat ${FILE} | grep -w 'num.query.type.NS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_MX=$(cat ${FILE} | grep -w 'num.query.type.MX' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TXT=$(cat ${FILE} | grep -w 'num.query.type.TXT' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_PTR=$(cat ${FILE} | grep -w 'num.query.type.PTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_AAAA=$(cat ${FILE} | grep -w 'num.query.type.AAAA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SRV=$(cat ${FILE} | grep -w 'num.query.type.SRV' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SOA=$(cat ${FILE} | grep -w 'num.query.type.SOA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HTTPS=$(cat ${FILE} | grep -w 'num.query.type.HTTPS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TYPE0=$(cat ${FILE} | grep -w 'num.query.type.TYPE0' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_CNAME=$(cat ${FILE} | grep -w 'num.query.type.CNAME' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_WKS=$(cat ${FILE} | grep -w 'num.query.type.WKS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HINFO=$(cat ${FILE} | grep -w 'num.query.type.HINFO' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_X25=$(cat ${FILE} | grep -w 'num.query.type.X25' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NAPTR=$(cat ${FILE} | grep -w 'num.query.type.NAPTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DS=$(cat ${FILE} | grep -w 'num.query.type.DS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DNSKEY=$(cat ${FILE} | grep -w 'num.query.type.DNSKEY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TLSA=$(cat ${FILE} | grep -w 'num.query.type.TLSA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SVCB=$(cat ${FILE} | grep -w 'num.query.type.SVCB' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SPF=$(cat ${FILE} | grep -w 'num.query.type.SPF' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_ANY=$(cat ${FILE} | grep -w 'num.query.type.ANY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_OTHER=$(cat ${FILE} | grep -w 'num.query.type.other' | cut -d '=' -f2)<br />
<br />
NUM_ANSWER_RCODE_NOERROR=$(cat ${FILE} | grep -w 'num.answer.rcode.NOERROR' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_NXDOMAIN=$(cat ${FILE} | grep -w 'num.answer.rcode.NXDOMAIN' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_SERVFAIL=$(cat ${FILE} | grep -w 'num.answer.rcode.SERVFAIL' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_REFUSED=$(cat ${FILE} | grep -w 'num.answer.rcode.REFUSED' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_nodata=$(cat ${FILE} | grep -w 'num.answer.rcode.nodata' | cut -d '=' -f2)<br />
NUM_ANSWER_secure=$(cat ${FILE} | grep -w 'num.answer.secure' | cut -d '=' -f2)<br />
<br />
# Sending info to zabbix_server, if variables is not empty!<br />
[ -z ${TOTAL_NUM_QUERIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.queries -o ${TOTAL_NUM_QUERIES}<br />
[ -z ${TOTAL_NUM_CACHEHITS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachehits -o ${TOTAL_NUM_CACHEHITS}<br />
[ -z ${TOTAL_NUM_CACHEMISS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachemiss -o ${TOTAL_NUM_CACHEMISS}<br />
[ -z ${TOTAL_NUM_PREFETCH} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.prefetch -o ${TOTAL_NUM_PREFETCH}<br />
[ -z ${TOTAL_NUM_RECURSIVEREPLIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.recursivereplies -o ${TOTAL_NUM_RECURSIVEREPLIES}<br />
<br />
[ -z ${TOTAL_REQ_MAX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.max -o ${TOTAL_REQ_MAX}<br />
[ -z ${TOTAL_REQ_AVG} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.avg -o ${TOTAL_REQ_AVG}<br />
[ -z ${TOTAL_REQ_OVERWRITTEN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.overwritten -o ${TOTAL_REQ_OVERWRITTEN}<br />
[ -z ${TOTAL_REQ_EXCEEDED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.exceeded -o ${TOTAL_REQ_EXCEEDED}<br />
[ -z ${TOTAL_REQ_CURRENT_ALL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.all -o ${TOTAL_REQ_CURRENT_ALL}<br />
[ -z ${TOTAL_REQ_CURRENT_USER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.user -o ${TOTAL_REQ_CURRENT_USER}<br />
<br />
[ -z ${TOTAL_TCPUSAGE} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.tcpusage -o ${TOTAL_TCPUSAGE}<br />
<br />
[ -z ${NUM_QUERY_TYPE_A} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.a -o ${NUM_QUERY_TYPE_A}<br />
[ -z ${NUM_QUERY_TYPE_NS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ns -o ${NUM_QUERY_TYPE_NS}<br />
[ -z ${NUM_QUERY_TYPE_MX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.mx -o ${NUM_QUERY_TYPE_MX}<br />
[ -z ${NUM_QUERY_TYPE_TXT} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.txt -o ${NUM_QUERY_TYPE_TXT}<br />
[ -z ${NUM_QUERY_TYPE_PTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ptr -o ${NUM_QUERY_TYPE_PTR}<br />
[ -z ${NUM_QUERY_TYPE_AAAA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.aaaa -o ${NUM_QUERY_TYPE_AAAA}<br />
[ -z ${NUM_QUERY_TYPE_SRV} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.srv -o ${NUM_QUERY_TYPE_SRV}<br />
[ -z ${NUM_QUERY_TYPE_SOA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.soa -o ${NUM_QUERY_TYPE_SOA}<br />
[ -z ${NUM_QUERY_TYPE_HTTPS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.https -o ${NUM_QUERY_TYPE_HTTPS}<br />
[ -z ${NUM_QUERY_TYPE_TYPE0} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.type0 -o ${NUM_QUERY_TYPE_TYPE0}<br />
[ -z ${NUM_QUERY_TYPE_CNAME} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.cname -o ${NUM_QUERY_TYPE_CNAME}<br />
[ -z ${NUM_QUERY_TYPE_WKS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.wks -o ${NUM_QUERY_TYPE_WKS}<br />
[ -z ${NUM_QUERY_TYPE_HINFO} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.hinfo -o ${NUM_QUERY_TYPE_HINFO}<br />
[ -z ${NUM_QUERY_TYPE_X25} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.X25 -o ${NUM_QUERY_TYPE_X25}<br />
[ -z ${NUM_QUERY_TYPE_NAPTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.naptr -o ${NUM_QUERY_TYPE_NAPTR}<br />
[ -z ${NUM_QUERY_TYPE_DS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ds -o ${NUM_QUERY_TYPE_DS}<br />
[ -z ${NUM_QUERY_TYPE_DNSKEY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.dnskey -o ${NUM_QUERY_TYPE_DNSKEY}<br />
[ -z ${NUM_QUERY_TYPE_TLSA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.tlsa -o ${NUM_QUERY_TYPE_TLSA}<br />
[ -z ${NUM_QUERY_TYPE_SVCB} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.svcb -o ${NUM_QUERY_TYPE_SVCB}<br />
[ -z ${NUM_QUERY_TYPE_SPF} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.spf -o ${NUM_QUERY_TYPE_SPF}<br />
[ -z ${NUM_QUERY_TYPE_ANY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.any -o ${NUM_QUERY_TYPE_ANY}<br />
[ -z ${NUM_QUERY_TYPE_OTHER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.other -o ${NUM_QUERY_TYPE_OTHER}<br />
<br />
[ -z ${NUM_ANSWER_RCODE_NOERROR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NOERROR -o ${NUM_ANSWER_RCODE_NOERROR}<br />
[ -z ${NUM_ANSWER_RCODE_NXDOMAIN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NXDOMAIN -o ${NUM_ANSWER_RCODE_NXDOMAIN}<br />
[ -z ${NUM_ANSWER_RCODE_SERVFAIL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.SERVFAIL -o ${NUM_ANSWER_RCODE_SERVFAIL}<br />
[ -z ${NUM_ANSWER_RCODE_REFUSED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.REFUSED -o ${NUM_ANSWER_RCODE_REFUSED}<br />
[ -z ${NUM_ANSWER_RCODE_nodata} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.nodata -o ${NUM_ANSWER_RCODE_nodata}<br />
[ -z ${NUM_ANSWER_secure} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.secure -o ${NUM_ANSWER_secure}<br />
No Zabbix será registrado dados como esses abaixo e posteriormente pode ser montado um Grafana com eles:<br />
[[Arquivo:Zabbix dns01.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns02.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns03.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns04.png|nenhum|commoldura]]<br />
<br />
== Balanceando o processamento e mantendo a hora certa ==<br />
Vamos instalar 2 programas agora, o IRQBalance e o Chrony. O primeiro para balancear a carga entre os cores e o segundo para manter a data e hora certas no sistema:<br />
# apt install irqbalance<br />
# systemctl enable irqbalance<br />
# apt install chrony<br />
Após a instalação do Chrony edite o arquivo /etc/chrony/chrony.conf, comente e a linha abaixo e adicione seus servidores NTP. Caso não tenha servidores NTP, estou colocando os do NIC.br aqui.<br />
#pool 2.debian.pool.ntp.org iburst<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
<br />
# systemctl restart chronyd.service<br />
Cheque com o '''chronyc''' se os servidores estão OK:<br />
# chronyc sourcestats<br />
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev<br />
==============================================================================<br />
a.st1.ntp.br 10 5 155m -0.027 0.030 -71us 51us<br />
b.st1.ntp.br 11 7 344m +0.068 0.079 +23ms 382us<br />
c.st1.ntp.br 6 3 344m +0.026 0.037 -124us 92us<br />
200.20.186.76 9 3 138m -0.022 0.031 +172us 42us<br />
<br />
# chronyc sources<br />
MS Name/IP address Stratum Poll Reach LastRx Last sample<br />
===============================================================================<br />
^* a.st1.ntp.br 1 10 377 588 +487us[ +397us] +/- 12ms<br />
^- b.st1.ntp.br 2 10 377 830 +23ms[ +23ms] +/- 49ms<br />
^+ c.st1.ntp.br 2 10 21 1038 -147us[ -242us] +/- 17ms<br />
^+ 200.20.186.76 1 10 377 1032 +381us[ +285us] +/- 15ms<br />
<br />
== Configurando o FRRouting ==<br />
Nesse ponto iremos configurar o '''FRRouting''' para enviar os IPs das '''loopbacks''' e o '''/30''' para o nosso PE do diagrama. Em '''/etc/frr/daemons''' habilite o parâmetro conforme abaixo:<br />
ospfd=yes<br />
Edite o arquivo '''/etc/frr/frr.conf''' e deixe com o conteúdo abaixo, para ficar conforme nosso diagrama do projeto. Apenas troque '''<SENHA>''' por uma senha para fechar o OSPF com mais segurança. Essa senha deve ser usada dos dois lados.<br />
frr version 8.2.2<br />
frr defaults traditional<br />
hostname dns-recursivo-01<br />
log syslog informational<br />
no ip forwarding<br />
no ipv6 forwarding<br />
service integrated-vtysh-config<br />
!<br />
interface ens18<br />
ip ospf message-digest-key 5 md5 '''<SENHA>'''<br />
ip ospf network point-to-point<br />
exit<br />
!<br />
router ospf<br />
ospf router-id 172.16.0.6<br />
network 10.10.10.10/32 area 0.0.0.0<br />
network 10.10.9.9/32 area 0.0.0.0<br />
network 172.16.0.4/30 area 0.0.0.0<br />
area 0 authentication message-digest<br />
exit<br />
!<br />
<br />
# systemctl restart frr.service<br />
Cheque se está tudo OK com o OSPF e verifique no PE se está recebendo os prefixos anunciados.<br />
# vtysh -c 'show ip ospf neighbor'<br />
<br />
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL<br />
172.16.0.5 1 Full/- 10m49s 35.310s 172.16.0.5 ens18:172.16.0.6 0 0 0<br />
<br />
# vtysh -c 'show ip ospf neighbor detail'<br />
<br />
Neighbor 172.16.0.5, interface address 172.16.0.5<br />
In the area 0.0.0.0 via interface ens18<br />
Neighbor priority is 1, State is Full/-, 5 state changes<br />
Most recent state change statistics:<br />
Progressive change 21w3d15h ago<br />
DR is 0.0.0.0, BDR is 0.0.0.0<br />
Options 18 *|-|-|EA|-|-|E|-<br />
Dead timer due in 34.685s<br />
Database Summary List 0<br />
Link State Request List 0<br />
Link State Retransmission List 0<br />
Thread Inactivity Timer on<br />
Thread Database Description Retransmision off<br />
Thread Link State Request Retransmission on<br />
Thread Link State Update Retransmission on<br />
<br />
Graceful restart Helper info:<br />
Graceful Restart HELPER Status : None<br />
<br />
== Configurando o Unbound ==<br />
Abaixo a configuração que usaremos nos servidores atentando para o detalhe do '''num-threads''', esse deve ter o valor igual ao número de CPUs do servidor.<br />
<br />
Também os IPs utilizados em '''outgoing-interface''' que serão diferentes em cada servidor, esses serão os IPs usados para '''recursividade'''. Consulte o manual do Unbound para obter mais informações sobre cada parâmetro listado na configuração.<br />
<br />
O tuning no Unbound pode ser alterado conforme abaixo:<br />
num-threads = nº CPUs<br />
so-reuseport = yes<br />
*-slabs = potência de 2 próximo ao num-threads<br />
msg-cache-size = 1G (quantidade de memória pra usar de cache)<br />
rrset-cache-size = 2 * msg-cache-size<br />
outgoing-range = 8192<br />
num-queries-per-thread = 4096<br />
so-rcvbuf e so-sndbuf = 4m ou 8m para servidores com muita requisição<br />
Agora vamos criar nosso arquivo de configuração base em '''/etc/unbound/unbound.conf.d/local.conf''':<br />
server:<br />
verbosity: 1<br />
statistics-interval: 0<br />
statistics-cumulative: no<br />
extended-statistics: yes<br />
num-threads: 6<br />
serve-expired: yes<br />
interface: 127.0.0.1<br />
interface: 10.10.10.10<br />
interface: 10.10.9.9<br />
interface: 172.16.0.6<br />
interface: ::1<br />
interface-automatic: no<br />
outgoing-interface: 198.18.1.10<br />
outgoing-interface: 2001:db8::faca:198:18:1:10<br />
outgoing-range: 8192<br />
outgoing-num-tcp: 1024<br />
incoming-num-tcp: 2048<br />
so-rcvbuf: 4m<br />
so-sndbuf: 4m<br />
so-reuseport: yes<br />
edns-buffer-size: 1232<br />
msg-cache-size: 1G<br />
msg-cache-slabs: 4<br />
num-queries-per-thread: 4096<br />
rrset-cache-size: 2G<br />
rrset-cache-slabs: 4<br />
infra-cache-slabs: 4<br />
do-ip4: yes<br />
do-ip6: yes<br />
do-udp: yes<br />
do-tcp: yes<br />
chroot: ""<br />
username: "unbound"<br />
directory: "/etc/unbound"<br />
logfile: "/var/log/unbound/unbound.log"<br />
use-syslog: no<br />
log-time-ascii: yes<br />
log-queries: no<br />
pidfile: "/var/run/unbound.pid"<br />
root-hints: "/usr/share/dns/root.hints"<br />
hide-identity: yes<br />
hide-version: yes<br />
unwanted-reply-threshold: 10000000<br />
prefetch: yes<br />
prefetch-key: yes<br />
rrset-roundrobin: yes<br />
minimal-responses: yes<br />
module-config: "respip validator iterator"<br />
val-clean-additional: yes<br />
val-log-level: 1<br />
key-cache-slabs: 4<br />
deny-any: yes<br />
access-control: 198.18.0.0/22 allow<br />
access-control: 2001:db8::/32 allow<br />
<br />
rpz:<br />
name: rpz.block.host.local.zone<br />
zonefile: /etc/unbound/rpz.block.hosts.zone<br />
rpz-action-override: nxdomain<br />
<br />
python:<br />
<br />
auth-zone:<br />
name: "."<br />
master: "b.root-servers.net"<br />
master: "c.root-servers.net"<br />
master: "d.root-servers.net"<br />
master: "f.root-servers.net"<br />
master: "g.root-servers.net"<br />
master: "k.root-servers.net"<br />
master: "lax.xfr.dns.icann.org"<br />
master: "iad.xfr.dns.icann.org"<br />
fallback-enabled: yes<br />
for-downstream: no<br />
for-upstream: yes<br />
zonefile: "/var/lib/unbound/root.zone"<br />
No parâmetro '''interface''' colocamos os IPs que serão usados para consulta dos clientes como o '''10.10.10.10''' e o '''10.10.9.9'''. Ali repare que coloquei também o IP privado '''172.16.0.6''', isso porque cada servidor terá o seu IP privado e este deve ser usado pelo seu sistema de monitoramento para checar cada servidor. No '''outgoing-interface''' teremos os IPs, tanto '''IPv4''' quanto '''IPv6''', para que seja feita a recursividade na Internet utilizando eles. Não tem '''IPv6''' ainda na sua rede? Dê uma olhada nesse artigo. Outro parâmetro importante é o '''access-control''' e é através dele que liberamos os prefixos IP para consultarem no nosso DNS Recursivo. No exemplo estou liberando todo o prefixo '''198.18.0.0/22''' e o prefixo '''2001:db8::/32'''. Além da ACL no Unbound, recomendo que crie um filtro de pacotes com iptables ou nftables protegendo seu sistema e liberando as portas '''53/UDP''', '''53/TCP''' e '''443/TCP''' apenas para seus clientes. Falarei sobre a '''443/TCP''' mais para frente nessa mesma documentação.<br />
<br />
Agora criaremos o arquivo '''RPZ''' ('''Response Policy Zones'''). Esse arquivo contém os sites que serão bloqueados via '''<abbr>DNS</abbr> Recursivo'''. São aqueles sites que às vezes você recebe um Ofício da Justiça solicitando o bloqueio deles. Não entrarei no mérito da efetividade desses bloqueios, porque muitos de vocês sabem que tecnicamente, existem formas de se fazer um bypass através desses bloqueios. Contudo vamos deixar nosso ambiente preparado para esses bloqueios e por isso crie o arquivo '''/etc/unbound/rpz.block.hosts.zone''' com esse conteúdo de exemplo:<br />
$TTL 2h<br />
@ IN SOA localhost. root.localhost. (2 6h 1h 1w 2h)<br />
IN NS localhost.<br />
; RPZ manual block hosts<br />
*.josedascoves.com CNAME .<br />
josedascoves.com CNAME .<br />
No exemplo acima estamos bloqueando qualquer consulta de DNS para '''josedascoves.com''' ou qualquer coisa '''.josedascoves.com'''.<br />
<br />
Para testar podemos fazer assim do próprio servidor:<br />
# host josedascoves.com ::1<br />
Using domain server:<br />
Name: ::1<br />
Address: ::1#53<br />
Aliases:<br />
<br />
Host josedascoves.com not found: 3(NXDOMAIN)<br />
Se a resposta for '''NXDOMAIN''' então está funcionando o bloqueio. Para incluir novos bloqueios basta adicionar os domínios, um abaixo do outro, conforme o exemplo que coloquei no arquivo RPZ.<br />
<br />
== Acertando o resolv.conf ==<br />
Vamos modificar nosso /etc/resolv.conf para utilizar DNS externo. Sim você deve estar se perguntando em qual situação isso seria utilizado. Primeiro entenda que o Unbound não irá utilizar o DNS externo para fazer as consultas na Internet e sim, qualquer teste que você faça do servidor precisará apontar para o Unbound usando os IPs '''127.0.0.1''' ou '''::1'''. Faremos isso pela seguinte situação: imagine que o daemon unbound morreu mas você ainda continua com conectividade na Internet. Você conseguiria acessar qualquer local na Internet através do IP mas não através do hostname porque não conseguiria resolver nomes, seu unbound estaria fora do ar. Imagine ainda que você gostaria que seu servidor te avisasse do problema via Telegram ou e-mail. Por isso estamos utilizando um DNS externo no '''/etc/resolv.conf''', apenas para essas situações. Se você não quiser utilizar desse recurso, pode usar o '''127.0.0.1''' e '''::1''' no lugar.<br />
nameserver 8.8.8.8<br />
nameserver 8.8.4.4<br />
nameserver 2001:4860:4860::8888<br />
<br />
== Script de teste de recursividade ==<br />
Estamos montando uma '''Rede de DNS Recursivo Anycast''', então é muito importante que você monitore essa rede para saber se algum node morreu e iniciar o troubleshooting, resolver o problema e levantar o sistema novamente. Tudo isso é importante mas o cliente não deve ficar esperando até você resolver o problema, seu sistema precisa ser inteligente o suficiente para se remover da Rede quando tiver um problema e se inserir novamente, quando o problema estiver sido solucionado. Se você montar uma Rede de DNS e um dos nodes apresentar algum problema, todos os clientes atendidos por aquele node migrarão automaticamente e transparentemente para outro '''DNS Recursivo Anycast''' mais próximo. Isso se chama '''disponibilidade'''.<br />
<br />
O script '''/root/scripts/checa_dns.sh''' abaixo tem a função de fazer os testes de recursividade e checar se o daemon do unbound continua rodando. Se algo acontecer, ele para o anúncio do '''10.10.10.10''' e '''10.10.9.9''' e retorna eles quando tudo estiver resolvido.<br />
# mkdir /root/scripts<br />
<br />
#!/usr/bin/env bash<br />
#Script para teste de recursividade v2.2<br />
# Por Marcelo Gondim<br />
# Em 24-12-2022<br />
#<br />
# checa_dns.sh is free software; you can redistribute it and/or modify<br />
# it under the terms of the GNU General Public License as published by<br />
# the Free Software Foundation; either version 2 of the License, or<br />
# (at your option) any later version.<br />
#<br />
# This program is distributed in the hope that it will be useful,<br />
# but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
# GNU General Public License for more details.<br />
#<br />
# You should have received a copy of the GNU General Public License<br />
# along with this program; if not, write to the Free Software<br />
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
#-----------------------------------------------------------------------<br />
#Informe um domínio por linha:<br />
dominios_testar=(<br />
www.google.com<br />
www.terra.com.br<br />
www.uol.com.br<br />
www.globo.com<br />
www.facebook.com<br />
www.youtube.com<br />
www.twitch.com<br />
www.discord.com<br />
www.debian.org<br />
www.redhat.com<br />
)<br />
corte_taxa_falha=100 #Porcentagem de falha para executar uma ação<br />
#-----------------------------------------------------------------------<br />
remove_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" != "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME morreu!" | /usr/local/sbin/telegram-notify --error --text -<br />
fi<br />
}<br />
<br />
adiciona_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" == "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME retornou do inferno!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
}<br />
<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
#echo "Servidor $HOSTNAME morreu DNS mas tentando levantar!" | /usr/local/sbin/telegram-notify --error --text -<br />
systemctl restart unbound<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
#echo "Servidor $HOSTNAME servico DNS voltou mas tinha morrido!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
<br />
qt_falhas=0<br />
qt_total="${#dominios_testar[@]}"<br />
echo "total_dominios: $qt_total"<br />
for site in "${dominios_testar[@]}"<br />
do<br />
resolver="127.0.0.1"<br />
echo -e " - dominio $site - $resolver - \c"<br />
host $site $resolver &> /dev/null<br />
if [ $? -ne 0 ]; then<br />
((qt_falhas++))<br />
echo -e "[Falhou]"<br />
else<br />
echo -e "[OK]"<br />
fi<br />
done<br />
<br />
taxa_falha=$((qt_falhas*100/qt_total))<br />
echo "Falhas $qt_falhas/$qt_total ($taxa_falha%)"<br />
<br />
if [ "$taxa_falha" -ge "$corte_taxa_falha" ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
adiciona_ospf<br />
Se rodarmos o script manualmente veremos isto:<br />
# /root/scripts/checa_dns.sh<br />
total_dominios: 10<br />
- dominio www.google.com - 127.0.0.1 - [OK]<br />
- dominio www.terra.com.br - 127.0.0.1 - [OK]<br />
- dominio www.uol.com.br - 127.0.0.1 - [OK]<br />
- dominio www.globo.com - 127.0.0.1 - [OK]<br />
- dominio www.facebook.com - 127.0.0.1 - [OK]<br />
- dominio www.youtube.com - 127.0.0.1 - [OK]<br />
- dominio www.twitch.com - 127.0.0.1 - [OK]<br />
- dominio www.discord.com - 127.0.0.1 - [OK]<br />
- dominio www.debian.org - 127.0.0.1 - [OK]<br />
- dominio www.redhat.com - 127.0.0.1 - [OK]<br />
Falhas 0/10 (0%)<br />
Se acontecer 100% de falhas o script irá remover os anúncios do OSPF. Se o daemon do unbound morrer, ele tentará reiniciá-lo. Se tudo normalizar o script irá retornar os anúncios para o OSPF. Deixei comentado no script as partes que enviariam uma notificação para o Telegram. Existem diversas documentações sobre isso na Internet, eu mesmo tenho uma. Assim que eu publicar aqui, atualizo essa documentação e sinta-se à vontade de modificar como desejar.<br />
# chmod 700 /root/scripts/checa_dns.sh<br />
Adicione a linha abaixo em seu '''/etc/crontab''':<br />
*/1 * * * * root /root/scripts/checa_dns.sh<br />
<br />
== Habilitando o DoH (<abbr>DNS</abbr> over HTTPS) - opcional ==<br />
Para habilitar o '''DoH''' no Unbound é bem simples mas só é possível com a versão '''1.17.1''' que vem no '''bullseye-backports''' e que virá na próxima versão do '''Debian 12 (Bookworm)'''. O recurso do '''DoH''' vem para trazer mais segurança e privacidade para o usuário. É um recurso muito pouco utilizado ainda mas que seu cliente pode vir a pedir algum dia.<br />
<br />
Você precisará gerar certificados SSL legítimos e para isso você poderá usar o '''Let's Encrypt''' só que de uma forma não tão convencional.<br />
<br />
Na sequência vamos instalar o Let's Encrypt para gerarmos nosso certificado SSL:<br />
# apt install letsencrypt<br />
Escolha um '''hostname''' para ser usado no nosso '''DoH''' e aponte ele no seu DNS Autoritativo para seus IPs 10.10.10.10 e 10.10.9.9. Aqui vamos usar o seguinte como exemplo: '''doh.brasilpeeringforum.org'''. Para gerarmos nosso certificado iremos usar o tipo '''DNS-01''', ele não necessita que tenhamos um servidor web rodando no servidor e nem tão pouco levanta um serviço na porta 80 para checar o hostname. Ele utiliza o DNS como validador e vai te solicitar que crie um registro '''CNAME''' no seu '''DNS Autoritativo''' para provar que você tem o controle sobre aquele hostname. Antes disso vamos instalar um programa em Python para podermos automatizar nossa renovação de certificado no futuro. Esse programa se encontra '''[https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py aqui]''' mas vou deixá-lo abaixo já modificado o interpretador.<br />
<br />
Crie o arquivo '''/etc/letsencrypt/acme-dns-auth.py''' com o conteúdo abaixo:<br />
#!/usr/bin/env python3<br />
import json<br />
import os<br />
import requests<br />
import sys<br />
<br />
### EDIT THESE: Configuration values ###<br />
<br />
# URL to acme-dns instance<br />
ACMEDNS_URL = "<nowiki>https://auth.acme-dns.io</nowiki>"<br />
# Path for acme-dns credential storage<br />
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"<br />
# Whitelist for address ranges to allow the updates from<br />
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]<br />
ALLOW_FROM = []<br />
# Force re-registration. Overwrites the already existing acme-dns accounts.<br />
FORCE_REGISTER = False<br />
<br />
### DO NOT EDIT BELOW THIS POINT ###<br />
### HERE BE DRAGONS ###<br />
<br />
DOMAIN = os.environ["CERTBOT_DOMAIN"]<br />
if DOMAIN.startswith("*."):<br />
DOMAIN = DOMAIN[2:]<br />
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN<br />
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]<br />
<br />
<br />
class AcmeDnsClient(object):<br />
"""<br />
Handles the communication with ACME-DNS API<br />
"""<br />
<br />
def __init__(self, acmedns_url):<br />
self.acmedns_url = acmedns_url<br />
<br />
def register_account(self, allowfrom):<br />
"""Registers a new ACME-DNS account"""<br />
<br />
if allowfrom:<br />
# Include whitelisted networks to the registration call<br />
reg_data = {"allowfrom": allowfrom}<br />
res = requests.post(self.acmedns_url+"/register",<br />
data=json.dumps(reg_data))<br />
else:<br />
res = requests.post(self.acmedns_url+"/register")<br />
if res.status_code == 201:<br />
# The request was successful<br />
return res.json()<br />
else:<br />
# Encountered an error<br />
msg = ("Encountered an error while trying to register a new acme-dns "<br />
"account. HTTP status {}, Response body: {}")<br />
print(msg.format(res.status_code, res.text))<br />
sys.exit(1)<br />
<br />
def update_txt_record(self, account, txt):<br />
"""Updates the TXT challenge record to ACME-DNS subdomain."""<br />
update = {"subdomain": account['subdomain'], "txt": txt}<br />
headers = {"X-Api-User": account['username'],<br />
"X-Api-Key": account['password'],<br />
"Content-Type": "application/json"}<br />
res = requests.post(self.acmedns_url+"/update",<br />
headers=headers,<br />
data=json.dumps(update))<br />
if res.status_code == 200:<br />
# Successful update<br />
return<br />
else:<br />
msg = ("Encountered an error while trying to update TXT record in "<br />
"acme-dns. \n"<br />
"------- Request headers:\n{}\n"<br />
"------- Request body:\n{}\n"<br />
"------- Response HTTP status: {}\n"<br />
"------- Response body: {}")<br />
s_headers = json.dumps(headers, indent=2, sort_keys=True)<br />
s_update = json.dumps(update, indent=2, sort_keys=True)<br />
s_body = json.dumps(res.json(), indent=2, sort_keys=True)<br />
print(msg.format(s_headers, s_update, res.status_code, s_body))<br />
sys.exit(1)<br />
<br />
class Storage(object):<br />
def __init__(self, storagepath):<br />
self.storagepath = storagepath<br />
self._data = self.load()<br />
<br />
def load(self):<br />
"""Reads the storage content from the disk to a dict structure"""<br />
data = dict()<br />
filedata = ""<br />
try:<br />
with open(self.storagepath, 'r') as fh:<br />
filedata = fh.read()<br />
except IOError as e:<br />
if os.path.isfile(self.storagepath):<br />
# Only error out if file exists, but cannot be read<br />
print("ERROR: Storage file exists but cannot be read")<br />
sys.exit(1)<br />
try:<br />
data = json.loads(filedata)<br />
except ValueError:<br />
if len(filedata) > 0:<br />
# Storage file is corrupted<br />
print("ERROR: Storage JSON is corrupted")<br />
sys.exit(1)<br />
return data<br />
<br />
def save(self):<br />
"""Saves the storage content to disk"""<br />
serialized = json.dumps(self._data)<br />
try:<br />
with os.fdopen(os.open(self.storagepath,<br />
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:<br />
fh.truncate()<br />
fh.write(serialized)<br />
except IOError as e:<br />
print("ERROR: Could not write storage file.")<br />
sys.exit(1)<br />
<br />
def put(self, key, value):<br />
"""Puts the configuration value to storage and sanitize it"""<br />
# If wildcard domain, remove the wildcard part as this will use the<br />
# same validation record name as the base domain<br />
if key.startswith("*."):<br />
key = key[2:]<br />
self._data[key] = value<br />
<br />
def fetch(self, key):<br />
"""Gets configuration value from storage"""<br />
try:<br />
return self._data[key]<br />
except KeyError:<br />
return None<br />
<br />
if __name__ == "__main__":<br />
# Init<br />
client = AcmeDnsClient(ACMEDNS_URL)<br />
storage = Storage(STORAGE_PATH)<br />
<br />
# Check if an account already exists in storage<br />
account = storage.fetch(DOMAIN)<br />
if FORCE_REGISTER or not account:<br />
# Create and save the new account<br />
account = client.register_account(ALLOW_FROM)<br />
storage.put(DOMAIN, account)<br />
storage.save()<br />
<br />
# Display the notification for the user to update the main zone<br />
msg = "Please add the following CNAME record to your main DNS zone:\n{}"<br />
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])<br />
print(msg.format(cname))<br />
<br />
# Update the TXT record in acme-dns instance<br />
client.update_txt_record(account, VALIDATION_TOKEN)<br />
<br />
# chmod +x /etc/letsencrypt/acme-dns-auth.py<br />
Usaremos a seguinte instrução para criar nosso certificado:<br />
# certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d doh.brasilpeeringforum.org<br />
Saving debug log to /var/log/letsencrypt/letsencrypt.log<br />
Plugins selected: Authenticator manual, Installer None<br />
Cert is due for renewal, auto-renewing...<br />
Renewing an existing certificate for doh.brasilpeeringforum.org<br />
Performing the following challenges:<br />
dns-01 challenge for doh.brasilpeeringforum.org<br />
Running manual-auth-hook command: /etc/letsencrypt/acme-dns-auth.py<br />
Output from manual-auth-hook command acme-dns-auth.py:<br />
Please add the following CNAME record to your main DNS zone:<br />
_acme-challenge.doh.brasilpeeringforum.org CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
<br />
Waiting for verification...<br />
<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Challenges loaded. Press continue to submit to CA. Pass "-v" for more info about<br />
challenges.<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Press Enter to Continue<br />
Nesse momento você cria o registro '''CNAME''' no seu DNS Autoritativo conforme ele solicitou: '''_acme-challenge.doh.brasilpeeringforum.org IN CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.''' e somente depois de criado e checado no DNS, você pressiona o '''Enter''' para continuar. Você pode checar dessa forma:<br />
# host -t cname _acme-challenge.doh.brasilpeeringforum.org<br />
_acme-challenge.doh.brasilpeeringforum.org is an alias for b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
Para que nosso certificado seja automaticamente renovado colocaremos no '''/etc/crontab''' a seguinte linha abaixo:<br />
00 00 1 * * root /usr/bin/certbot -q renew<br />
Acima temos a instrução para renovação automática do certificado. Repare que você vai precisar também copiar esse certificado para seus outros servidores, escolha um servidor para manter o certificado sempre atualizado e crie um script que faça a mesma cópia remotamente para os outros servidores. O '''scp''' e o '''rsync''' são seus aliados nisso.<br />
<br />
=== Configurando o Unbound ===<br />
Em nosso '''/etc/unbound/unbound.conf.d/local.conf''', adicionaremos no bloco "'''server:'''" o seguinte:<br />
interface: 10.10.10.10@443<br />
interface: 10.10.9.9@443<br />
tls-service-key: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/privkey.pem"<br />
tls-service-pem: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/fullchain.pem"<br />
Para usar o recurso do '''DoH''' você precisará habilitar o recurso no seu navegador e informar a URL. Vou colocar o exemplo do '''Google Chrome''': Digite '''chrome://settings/security?search=dns''' no seu Chrome e ative '''Usar DNS seguro''', selecione '''Personalizado''' e adicione nossa URL:<br />
[[Arquivo:Doh bpf2.png|nenhum|commoldura]]<br />
<br />
== Finalizando ==<br />
Aqui finalizamos nosso projeto para uma Rede de DNS(s) Recursivos Anycast com Hyperlocal. Esse projeto é escalável, seguro, resiliente e você entregará muito mais qualidade de Internet para o seu cliente. Pare de entregar o '''8.8.8.8''' para os seus clientes, você está contribuindo para uma Internet mais lenta, sem a qualidade que o seu cliente merece. Investi meu tempo, que é muito pouco, para deixar esse documento para a comunidade, para você melhorar o seu ISP, para dar um UP! nele, então vamos começar 2023 com o pé direito. O que acha?<br />
<br />
Como prova de conceito, uma imagem abaixo onde temos uma Rede em produção de DNS(s) Recursivos Anycast e apontando exatamente o momento em que houve alguma situação que fez com que as queries de DNS, convergissem de um node para outro, de forma transparente e automática para o cliente. Podemos notar também que ao ser resolvido o problema, o tráfego retornou para o seu node correto:<br />
[[Arquivo:Convergencia.png|nenhum|commoldura]]<br />
<br />
== KINDNS (Stands for Knowledge-Sharing and Instantiating Norms for DNS and Naming Security) ==<br />
Achou que havia terminado? Agora que você tem a capacidade de montar uma '''Rede de DNS Recursivo''' com todas essas features acima, com todas as ferramentas que foram comentadas, o que acha de certificar o que fez?<br />
<br />
Assim como o [https://www.manrs.org/ MANRS] veio para certificar nosso sistema de roteamento na Internet, agora temos o [https://kindns.org/ KINDNS] para certificar que nossos sistemas de DNS estão bem feitos e dentro dos padrões de segurança. Existem '''7 ações''' que podem ser certificadas para nossos DNS Recursivos e estão aqui em https://kindns.org/shared-private-resolvers/. Com essa nossa documentação, se bem aplicada, você pode se candidatar ao KINDNS e ter seu ASN listado aqui https://kindns.org/participants/<br />
<br />
Obter e manter o '''MANRS''' e '''KINDNS''' demonstra seu compromisso com as Boas Práticas, contribui para termos uma '''Internet''' mais segura e te abre portas para novos negócios que possam exigir essas conformidades.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]<br />
__FORCARTDC__</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=DNS_Recursivo_Anycast_Hyperlocal&diff=3664DNS Recursivo Anycast Hyperlocal2023-12-30T17:31:40Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
==Introdução==<br />
Você sabe como funciona a Internet? Essa é uma pergunta que meu amigo '''Thiago Ayub''' sempre faz aos seus candidatos à vagas de emprego e não importa o quanto tenham de experiência em '''Engenharia de Redes''', todos sempre travam nesse momento. Todos estão sempre prontos e preparados para resolver os problemas mais cabeludos em '''BGP''', '''OSPF''', '''MPLS''', etc mas travam com essa simples pergunta. Para contextualizar e visualizarmos melhor vamos nos atentar à imagem abaixo e uma explicação simplificada de como funciona:<br />
[[Arquivo:Dns hierarquia.png|esquerda|commoldura]]<br />
Tudo começa com um usuário sentado confortavelmente e querendo acessar um conteúdo disponível na Internet. Ele digita em seu navegador preferido a URL: '''<nowiki>https://wiki.brasilpeeringforum.org</nowiki>''',<br />
<br />
'''<big>1)</big>''' <big>O</big> <big>navegador irá requisitar do '''DNS Recursivo''' utilizado pelo usuário, o '''endereço IP''' que responde pelo nome '''wiki.'''</big>'''brasilpeeringforum.org'''<big>. Isso porque todos os acessos se dão na Internet através do '''endereço''' '''IP''' e não através do '''nome'''. Imaginem se tivéssemos que decorar os endereços IPs de todos os sites e serviços que quiséssemos acessar na Internet?</big> <br />
<br />
<big>'''2)''' Nosso DNS Recursivo checa se a informação consta em seu cache.</big> Se a informação existir ela é devolvida ao navegador do usuário e aí este consegue acessar o site.<br />
<br />
'''3)''' Do contrário o DNS Recursivo pergunta ao '''Root Server''' quem é o '''TLD (Top Level Domain)''' responsável para atender a requisição. <br />
<br />
'''4)''' O '''Root Server''' informa ao DNS Recursivo o endereço do '''TLD responsável'''. No Brasil o '''TLD''' responsável pelo '''.br''' seria o '''Registro.br'''.<br />
<br />
'''5)''' O DNS Recursivo pergunta ao '''TLD''' sobre '''wiki.brasilpeeringforum.org''' e este responde com os endereços IP dos '''DNS Autoritativos''' responsáveis pelo domínio '''brasilpeeringforum.org.'''<br />
<br />
'''6)''' O DNS Recursivo pergunta aos '''DNS Autoritativos''' pelo '''wiki.brasilpeeringforum.org''' e este responde com o '''endereço IP'''.<br />
<br />
'''7)''' Por último o DNS Recursivo devolve a informação para o navegador do usuário.<br />
<br />
Como que se dá a comunicação entre os '''DNS(s) Recursivos, Root Servers, TLDs''' e '''Autoritativos'''? Como que o navegador do usuário, após receber o IP do site, consegue chegar no servidor que tem o conteúdo? Isso só é possível devido ao protocolo chamado '''BGP (Border Gateway Protocol)''', todos os caminhos que conhecemos como rotas de destino, são anunciadas por milhares de participantes na '''Internet''' conhecidos como '''AS (Autonomous System)''', esses participantes se interligam para disponibilizar conteúdos e acessos pelo mundo aos milhares de usuários. É uma imensa rede colaborativa formada por Empresas, Universidades, Governos e todos que queiram se interconectar. Percebam que sem o '''BGP''', que serve de caminho para chegarmos nos conteúdos e sem o '''DNS (Domain Name System)''' para traduzir o nome para o endereço IP, a '''Internet''' não funcionaria e por isso precisamos cuidar muito bem desses dois serviços.<br />
<br><br><br><br><br />
Mas não acaba por aí. O '''DNS Recursivo''' tem um papel muito importante para o Provedor de Internet e que envolve segurança, qualidade de acesso à Internet e a disponibilidade do serviço entregue ao cliente. Quando bem configurado acelera as consultas dos acessos graças ao seu cache interno, mas para que isso seja percebido pelo assinante, é necessário que esteja o mais próximo possível do seu cliente.<br />
<br><br />
== Um erro que destrói a qualidade do nosso serviço ==<br />
Um erro muito comum que muitas operadoras cometem é utilizar DNS Recursivo externo, como o '''8.8.8.8''', '''1.1.1.1''' e outros, para seus clientes. Quanto mais próximo dos seus clientes, mais qualidade de serviço estará entregando a eles. Conteúdos serão entregues mais rapidamente pois serão resolvidos e armazenados em caches locais e não consultados remotamente na Internet. Para falar mais sobre isso, te convido leitor desse documento, que assista essa palestra do '''Thiago Ayub''' no '''GTER 51/GTS 37''' (2022) '''8.888 MOTIVOS PARA NÃO USAR DNS RECURSIVO EXTERNO EM SEU AS''': https://www.youtube.com/watch?v=Rsvpu5uF2Io<br />
<br />
== Objetivo ==<br />
O objetivo desta documentação não é te ensinar tudo sobre '''DNS''', '''BGP''', '''OSPF''' e nem tão pouco sobre GNU/Linux e sim te mostrar um exemplo de servidor DNS Recursivo implementado pensando em segurança, qualidade e resiliência. Usaremos em todas as nossas documentações o [https://www.debian.org/ Debian GNU/Linux], por ser uma distribuição que considero uma obra de arte criada por uma enorme comunidade séria, com vasta experiência de anos, qualidade no empacotamento dos programas, estável e com uma equipe de segurança excelente e ativa. Caso você leitor, utilize alguma outra distribuição GNU/Linux, todo conteúdo apresentado aqui pode ser aplicado em outras distros, desde que respeitando as particularidades de cada uma.<br />
<br />
Aqui construiremos um sistema do tipo '''Anycast''', ou seja, terás o serviço rodando em diversas localidades da sua Rede utilizando o mesmo endereçamento IP e que atenderá seu cliente mais próximo. Em caso de falhas, seus clientes automaticamente e de forma transparente continuarão consultando o DNS mais próximo deles. Para que ele funcione dessa forma você precisará ter uma '''Rede OSPF''' implementada no seu Provedor Internet ou algum outro protocolo como por exemplo o '''ISIS,''' mas esse documento não irá abordar o '''ISIS'''. Também utilizaremos o '''Hyperlocal''' como recurso adicional para gerar algumas proteções de segurança e velocidade na resposta relacionada aos servidores de DNS Raiz da Internet.<br />
<br />
== Diagrama ==<br />
Para exemplificar nosso servidor de DNS Recursivo, usaremos como base das explicações um diagrama demonstrando o uso do DNS Recursivo em uma Rede fictícia. Adotaremos IPs privados e reservados para demonstrar todo o ambiente do Provedor de Internet.<br />
[[Arquivo:Diagrama dns recursivo.drawio.png|esquerda|commoldura]]<br />
Nesse diagrama podemos observar alguns detalhes técnicos como por exemplo: existem '''3 servidores de DNS Recursivo''' posicionados em locais diferentes, que poderiam estar em bairros diferentes e até em cidades diferentes. Em cada servidor teremos '''2 loopbacks''' com os IPs:<br />
<br />
'''10.10.10.10/32'''<br />
<br />
'''10.10.9.9/32'''<br />
<br />
Esses IPs serão entregues pelos concentradores '''PPPoE''' ou '''IPoE''' ('''BNG''') para seus clientes como '''DNS primário''' e '''secundário'''. Podemos usar IPs privados como DNS primário e secundário em um ambiente real? Sim podemos, desde que não sejam IPs que possam ter problemas com as redes privadas dos clientes. Ex.: rede do cliente usando '''192.168.0.0/24'''. Se entregarmos o DNS sendo '''192.168.0.10''' e '''192.168.0.20''' teremos problemas e o cliente ficará sem Internet, porque '''192.168.0.10''' e '''192.168.0.20''' fazem parte da rede '''192.168.0.0/24'''.<br />
<br />
Agora entregando '''10.10.10.10''' e '''10.10.9.9''' não teríamos problemas com a rede '''192.168.0.0/24'''.<br />
<br />
'''Motivos para usarmos IPs privados:'''<br />
* O principal motivo está relacionado com a segurança, uma vez que sendo um IP privado, não pode sofrer ataques DDoS direcionados diretamente para ele, vindos da Internet.<br />
* Nem mesmo o cliente da sua rede conhece os '''IPs públicos''' utilizados para recursividade na Internet.<br />
* Memorizar os IPs '''10.10.10.10''' e '''10.10.9.9''' é tão fácil quanto memorizar o '''8.8.8.8''' e o '''1.1.1.1'''. Mais fácil para o seu técnico guardar essa informação e utilizar onde for necessário.<br />
Cada servidor DNS Recursivo possui um '''IPv4 público''', aqui representado por '''198.18.x.x/27''' e um '''IPv6 global''' representado por um IP dentro do prefixo '''2001:db8::/32'''. Cada servidor precisa ter os seus próprios IPs e são através destes IPs que as consultas de DNS serão realizadas na Internet.<br />
<br />
Nessa topologia usando '''Anycast''', o cliente será sempre atendido pelo '''DNS Recursivo''' mais próximo, desde que os pesos no '''OSPF''' estejam ajustados corretamente.<br />
<br><br><br><br><br><br><br />
== Dados do servidor ==<br />
Podemos utilizar um sistema virtualizado ou não. Sistemas virtualizados são bem vindos pois são mais simples quando precisamos fazer backups, levantar outros sistemas sem complicações e se precisarmos restaurar rapidamente algum sistema que ficou indisponível por algum motivo. A configuração abaixo tem capacidade para atender algo em torno a '''50.000 assinantes ou mais'''. O DNS Recursivo é um serviço que pode ser utilizado até mesmo em um '''Raspberry Pi''' e atender operações pequenas, nesse caso com o intuito de economizar energia e espaço. Nosso foco aqui é montar uma rede de '''DNS Recursivo Anycast com HyperLocal'''. Como comentei acima o servidor deve ficar o mais próximo dos clientes para termos a '''menor latência possível''' e '''sempre menor que 5ms''' entre o cliente e o servidor.<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Memória<br />
!Disco<br />
!Sistema<br />
|-<br />
|2.4Ghz 6 cores<br />
|16G DDR4<br />
|30G<br />
|Debian 11 amd64 (Bullseye)<br />
|}<br />
<br />
== Softwares utilizados ==<br />
* Debian 11 amd64 (Bullseye) instalação mínima.<br />
<br />
* [https://frrouting.org/ FRRouting].<br />
* Unbound.<br />
* IRQBalance.<br />
* Chrony (NTP/NTS).<br />
* Shell script em bash.<br />
<br />
== Funcionalidades que teremos ==<br />
* Sistema em Anycast.<br />
* Hyperlocal.<br />
* Controle de acesso por <abbr>ACL</abbr>.<br />
* RPZ (Response Policy Zone).<br />
* Bloqueio de consultas do tipo ANY.<br />
* QNAME minimization habilitado. (habilitado por default no Unbound)<br />
* Recursividade em IPv4 e IPv6.<br />
* DNSSEC habilitado.<br />
* <abbr>DoH (DNS</abbr> over HTTPS) habilitado.<br />
<br />
== Monitoramento ==<br />
O monitoramento é algo bem específico e não é o foco deste documento mas é extremamente importante que você monitore seus servidores de DNS por alguma ferramenta como o Zabbix. Aqui mostrarei apenas como enviar as informações para o Zabbix. Algumas coisas que você deveria monitorar nos servidores de DNS Recursivo:<br />
* Serviço do unbound parou.<br />
* Perda de pacotes.<br />
* Latência alta de pacotes.<br />
* Lentidão na resolução de queries.<br />
* CPU alta.<br />
* Load alto.<br />
* Memória com uso alto.<br />
* Disco com pouco espaço.<br />
* Queda brusca nas queries.<br />
* A recursividade parou de funcionar.<br />
* A recursividade voltou a funcionar.<br />
Este abaixo é um exemplo de monitoramento de um sistema de DNS Recursivo que atende 50.000 assinantes:<br />
[[Arquivo:Grafana dns.png|nenhum|commoldura]]<br />
<br />
== Configurando a Rede ==<br />
Nossa documentação será baseada no diagrama apresentado acima e por isso configuraremos apenas um dos três servidores, porque os outros serão configurados da mesma forma, só que com dados diferentes. Para tanto assumirei que já temos um sistema Debian instalado com o mínimo de pacotes e somente com sshd, para que possamos acessar remotamente mais tarde. '''Não instale um ambiente gráfico no servidor''', você não deve querer fazer isso por diversos motivos e os principais: primeiro porque não é um Desktop e segundo porque o ambiente gráfico devoraria toda a memória com recursos que não seriam úteis aqui.<br />
<br />
Em '''/etc/network/interfaces''' deixaremos assim:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto lo:0<br />
iface lo:0 inet static<br />
address 10.10.10.10/32<br />
<br />
auto lo:1<br />
iface lo:1 inet static<br />
address 10.10.9.9/32<br />
<br />
# The primary network interface<br />
auto ens18<br />
iface ens18 inet static<br />
address 198.18.1.10/27<br />
gateway 198.18.1.1<br />
<br />
iface ens18 inet6 static<br />
address 2001:db8::faca:198:18:1:10/64<br />
gateway 2001:db8::faca:198:18:1:1<br />
<br />
# The secondary network interface<br />
auto ens18:0<br />
iface ens18:0 inet static<br />
address 172.16.0.6/30<br />
Nesse cenário temos as duas '''loopbacks''' com os IPs '''10.10.10.10''' e '''10.10.9.9''' que serão anunciados via OSPF para a rede e serem entregues aos clientes via BNG. Os IPs '''198.18.1.10''' e '''2001:db8::faca:198:18:1:10''' serão usados para fazerem a recursividade na Internet tanto em IPv4 quanto em IPv6. Esses IPs não devem ser divulgados para clientes; os IPs públicos são dedicados apenas para essa finalidade.<br />
<br />
== Configuração dos repositórios Debian ==<br />
Deixe o arquivo '''/etc/apt/sources.list''' conforme abaixo:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
Após a configuração vamos instalar alguns pacotes necessários e outros úteis:<br />
# apt update && apt full-upgrade<br />
# apt install net-tools nftables htop iotop sipcalc tcpdump curl gnupg rsync wget host dnsutils mtr-tiny bmon sudo tmux whois ethtool dnstop<br />
<br />
== Fazendo algum tuning no sistema ==<br />
Em '''/etc/sysctl.conf''' adicionamos no final do arquivo essas instruções:<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
Estamos fazendo algumas melhorias de memória, algumas relacionadas a '''conntrack''' porque se for usar um filtro de pacotes stateful, como o '''Netfilter/IPTables''' ou '''Netfilter/NFTables''', o valor default da tabela é pequeno e dependendo da situação, se estourar essa tabela, as consultas de DNS terão problemas também. O DNS Recursivo não deve ficar aberto para qualquer um na Internet, ele deve ser liberado apenas para seus clientes. Podemos fazer através das ACLs do Unbound e pelo filtro de pacotes. O último parâmetro diz respeito ao uso de swap, por padrão o Debian permite o uso de swap após 40% do uso da memória, nesse caso estamos dizendo para o sistema usar o swap com 90% de uso da memória.<br />
<br />
Precisamos adicionar o módulo '''nf_conntrack''' em '''/etc/modules''' para que seja carregado em tempo de boot, senão os parâmetros de '''conntrack''' que colocamos em '''/etc/sysctl.conf''' não serão carregados.<br />
# echo nf_conntrack >> /etc/modules<br />
# modprobe nf_conntrack<br />
# sysctl -p<br />
<br />
== Instalando o FRRouting ==<br />
O FRRouting é o programa que usaremos para fazer os anúncios das nossas loopbacks via OSPF. Nesse documento usaremos a versão 8.x e para isso precisaremos configurar o repositório oficial do FRRouting e instalar os pacotes:<br />
# echo "deb <nowiki>https://deb.frrouting.org/frr</nowiki> bullseye frr-8" > /etc/apt/sources.list.d/frr.list<br />
# curl -s <nowiki>https://deb.frrouting.org/frr/keys.asc</nowiki> | apt-key add -<br />
# apt update<br />
# apt install frr frr-doc frr-pythontools<br />
Aconselho depois de instalar os pacotes, marcá-los para não atualizar juntamente com os demais pacotes, isso é para evitar de ocorrer alguma atualização no FRRouting, que torne o serviço instável por algum motivo. Não que isso vá ocorrer, mas é melhor fazer essa atualização quando realmente for necessário.<br />
# apt-mark hold frr frr-doc frr-pythontools<br />
Após esse comando acima, o sistema manterá a instalação original do pacote intacta. Para desbloquear basta executar o comando abaixo:<br />
# apt-mark unhold frr frr-doc frr-pythontools<br />
<br />
== Removendo o APPARMOR ==<br />
O '''APPARMOR''' às vezes causa mais problemas que solução e se não for fazer uma completa configuração nele, é melhor desabilitá-lo. Para fazer isso efetivamente, o procedimento é esse abaixo:<br />
# mkdir -p /etc/default/grub.d<br />
# echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=0"' | tee /etc/default/grub.d/apparmor.cfg<br />
# update-grub<br />
# reboot<br />
<br />
== Instalando o Unbound ==<br />
Nesse momento ainda não iremos configurar o Unbound, apenas instalar o pacote e acertar o ambiente. Vamos instalar o unbound do backports porque este já possui suporte ao DoH que veremos mais à frente.<br />
# apt -t bullseye-backports install unbound dns-root-data<br />
# mkdir -p /var/log/unbound<br />
# touch /var/log/unbound/unbound.log<br />
# chown -R unbound:unbound /var/log/unbound/<br />
# cd /etc/unbound<br />
# wget -c <nowiki>ftp://FTP.INTERNIC.NET/domain/named.cache</nowiki><br />
# systemctl restart unbound<br />
Configurando o logrotate:<br />
cat << EOF > /etc/logrotate.d/unbound<br />
/var/log/unbound/unbound.log {<br />
rotate 5<br />
weekly<br />
postrotate<br />
unbound-control log_reopen<br />
endscript<br />
}<br />
EOF<br />
Reiniciando o serviço:<br />
# systemctl restart logrotate.service<br />
<br />
== Preparando o monitoramento do seu DNS Recursivo ==<br />
O monitoramento do seu DNS Recursivo é muito importante e para isso vamos usar um '''template para Zabbix''', que modifiquei juntamente com o seu shell script e que enviará os dados para o seu Zabbix server via '''zabbix-sender'''. O projeto original está aqui '''https://github.com/jeftedelima/Unbound-DNS<nowiki/>.''' O xml alterado está aqui '''https://github.com/gondimcodes/template_zabbix_dns_unbound'''. Embora seja antigo é perfeitamente importável no Zabbix 6.0, por exemplo.<br />
<br />
'''<nowiki/>'''<br />
<br />
Teremos um shell script que você precisará colocar no seu '''/etc/crontab'''. No exemplo abaixo assumi que o shell script está em '''/root/scripts'''. De 5 em 5 minutos os dados serão enviados para o seu Zabbix server.<br />
*/5 * * * * root /root/scripts/unboundSend.sh '''IP_zabbix_server''' '''nome_do_host''' 1> /dev/null<br />
Na linha acima, troque o '''IP_zabbix_server''' pelo '''IP do seu servidor Zabbix''' e o '''nome_do_host''' pelo '''hostname''' '''do seu DNS Recursivo'''. Você precisará instalar o pacote '''zabbix-sender''' no seu DNS Recursivo pois ele será usado para enviar os dados para o Zabbix server.<br />
<br />
Abaixo o '''unboundSend.sh''' também alterado com inclusão de mais dados:<br />
#!/bin/bash<br />
# @Jefte de Lima Ferreira<br />
# jeftedelima at gmail dot com<br />
# CRON Example<br />
# Contributor: Marcelo Gondim - gondim at gmail dot com<br />
# */5 **** root sh /home/dir/unboundSend.sh 192.168.10.1 Unbound 1> /dev/null<br />
<br />
if [ -z ${1} ] || [ -z ${2} ] ; then<br />
echo "You need to specify the IP address of zabbix server and hostname of your DNS Unbound on zabbix"<br />
echo "Usage example: ./unboundSend.sh 192.168.10.1 UnboundServer"<br />
exit 1<br />
fi<br />
<br />
# ZABBIX_SERVER IP<br />
IP_ZABBIX=$1<br />
# NAME Unbound on Zabbix<br />
NAME_HOST=$2<br />
DIR_TEMP=/var/tmp/<br />
FILE="${DIR_TEMP}dump_unbound_control_stats.txt"<br />
unbound-control stats > ${FILE}<br />
<br />
TOTAL_NUM_QUERIES=$(cat ${FILE} | grep -w 'total.num.queries' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEHITS=$(cat ${FILE} | grep -w 'total.num.cachehits' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEMISS=$(cat ${FILE} | grep -w 'total.num.cachemiss' | cut -d '=' -f2)<br />
TOTAL_NUM_PREFETCH=$(cat ${FILE} | grep -w 'total.num.prefetch' | cut -d '=' -f2)<br />
TOTAL_NUM_RECURSIVEREPLIES=$(cat ${FILE} | grep -w 'total.num.recursivereplies' | cut -d '=' -f2)<br />
<br />
TOTAL_REQ_MAX=$(cat ${FILE} | grep -w 'total.requestlist.max' | cut -d '=' -f2)<br />
TOTAL_REQ_AVG=$(cat ${FILE} | grep -w 'total.requestlist.avg' | cut -d '=' -f2)<br />
TOTAL_REQ_OVERWRITTEN=$(cat ${FILE} | grep -w 'total.requestlist.overwritten' | cut -d '=' -f2)<br />
TOTAL_REQ_EXCEEDED=$(cat ${FILE} | grep -w 'total.requestlist.exceeded' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_ALL=$(cat ${FILE} | grep -w 'total.requestlist.current.all' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_USER=$(cat ${FILE} | grep -w 'total.requestlist.current.user' | cut -d '=' -f2)<br />
<br />
TOTAL_TCPUSAGE=$(cat ${FILE} | grep -w 'total.tcpusage' | cut -d '=' -f2)<br />
<br />
NUM_QUERY_TYPE_A=$(cat ${FILE} | grep -w 'num.query.type.A' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NS=$(cat ${FILE} | grep -w 'num.query.type.NS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_MX=$(cat ${FILE} | grep -w 'num.query.type.MX' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TXT=$(cat ${FILE} | grep -w 'num.query.type.TXT' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_PTR=$(cat ${FILE} | grep -w 'num.query.type.PTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_AAAA=$(cat ${FILE} | grep -w 'num.query.type.AAAA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SRV=$(cat ${FILE} | grep -w 'num.query.type.SRV' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SOA=$(cat ${FILE} | grep -w 'num.query.type.SOA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HTTPS=$(cat ${FILE} | grep -w 'num.query.type.HTTPS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TYPE0=$(cat ${FILE} | grep -w 'num.query.type.TYPE0' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_CNAME=$(cat ${FILE} | grep -w 'num.query.type.CNAME' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_WKS=$(cat ${FILE} | grep -w 'num.query.type.WKS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HINFO=$(cat ${FILE} | grep -w 'num.query.type.HINFO' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_X25=$(cat ${FILE} | grep -w 'num.query.type.X25' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NAPTR=$(cat ${FILE} | grep -w 'num.query.type.NAPTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DS=$(cat ${FILE} | grep -w 'num.query.type.DS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DNSKEY=$(cat ${FILE} | grep -w 'num.query.type.DNSKEY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TLSA=$(cat ${FILE} | grep -w 'num.query.type.TLSA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SVCB=$(cat ${FILE} | grep -w 'num.query.type.SVCB' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SPF=$(cat ${FILE} | grep -w 'num.query.type.SPF' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_ANY=$(cat ${FILE} | grep -w 'num.query.type.ANY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_OTHER=$(cat ${FILE} | grep -w 'num.query.type.other' | cut -d '=' -f2)<br />
<br />
NUM_ANSWER_RCODE_NOERROR=$(cat ${FILE} | grep -w 'num.answer.rcode.NOERROR' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_NXDOMAIN=$(cat ${FILE} | grep -w 'num.answer.rcode.NXDOMAIN' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_SERVFAIL=$(cat ${FILE} | grep -w 'num.answer.rcode.SERVFAIL' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_REFUSED=$(cat ${FILE} | grep -w 'num.answer.rcode.REFUSED' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_nodata=$(cat ${FILE} | grep -w 'num.answer.rcode.nodata' | cut -d '=' -f2)<br />
NUM_ANSWER_secure=$(cat ${FILE} | grep -w 'num.answer.secure' | cut -d '=' -f2)<br />
<br />
# Sending info to zabbix_server, if variables is not empty!<br />
[ -z ${TOTAL_NUM_QUERIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.queries -o ${TOTAL_NUM_QUERIES}<br />
[ -z ${TOTAL_NUM_CACHEHITS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachehits -o ${TOTAL_NUM_CACHEHITS}<br />
[ -z ${TOTAL_NUM_CACHEMISS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachemiss -o ${TOTAL_NUM_CACHEMISS}<br />
[ -z ${TOTAL_NUM_PREFETCH} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.prefetch -o ${TOTAL_NUM_PREFETCH}<br />
[ -z ${TOTAL_NUM_RECURSIVEREPLIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.recursivereplies -o ${TOTAL_NUM_RECURSIVEREPLIES}<br />
<br />
[ -z ${TOTAL_REQ_MAX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.max -o ${TOTAL_REQ_MAX}<br />
[ -z ${TOTAL_REQ_AVG} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.avg -o ${TOTAL_REQ_AVG}<br />
[ -z ${TOTAL_REQ_OVERWRITTEN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.overwritten -o ${TOTAL_REQ_OVERWRITTEN}<br />
[ -z ${TOTAL_REQ_EXCEEDED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.exceeded -o ${TOTAL_REQ_EXCEEDED}<br />
[ -z ${TOTAL_REQ_CURRENT_ALL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.all -o ${TOTAL_REQ_CURRENT_ALL}<br />
[ -z ${TOTAL_REQ_CURRENT_USER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.user -o ${TOTAL_REQ_CURRENT_USER}<br />
<br />
[ -z ${TOTAL_TCPUSAGE} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.tcpusage -o ${TOTAL_TCPUSAGE}<br />
<br />
[ -z ${NUM_QUERY_TYPE_A} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.a -o ${NUM_QUERY_TYPE_A}<br />
[ -z ${NUM_QUERY_TYPE_NS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ns -o ${NUM_QUERY_TYPE_NS}<br />
[ -z ${NUM_QUERY_TYPE_MX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.mx -o ${NUM_QUERY_TYPE_MX}<br />
[ -z ${NUM_QUERY_TYPE_TXT} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.txt -o ${NUM_QUERY_TYPE_TXT}<br />
[ -z ${NUM_QUERY_TYPE_PTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ptr -o ${NUM_QUERY_TYPE_PTR}<br />
[ -z ${NUM_QUERY_TYPE_AAAA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.aaaa -o ${NUM_QUERY_TYPE_AAAA}<br />
[ -z ${NUM_QUERY_TYPE_SRV} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.srv -o ${NUM_QUERY_TYPE_SRV}<br />
[ -z ${NUM_QUERY_TYPE_SOA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.soa -o ${NUM_QUERY_TYPE_SOA}<br />
[ -z ${NUM_QUERY_TYPE_HTTPS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.https -o ${NUM_QUERY_TYPE_HTTPS}<br />
[ -z ${NUM_QUERY_TYPE_TYPE0} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.type0 -o ${NUM_QUERY_TYPE_TYPE0}<br />
[ -z ${NUM_QUERY_TYPE_CNAME} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.cname -o ${NUM_QUERY_TYPE_CNAME}<br />
[ -z ${NUM_QUERY_TYPE_WKS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.wks -o ${NUM_QUERY_TYPE_WKS}<br />
[ -z ${NUM_QUERY_TYPE_HINFO} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.hinfo -o ${NUM_QUERY_TYPE_HINFO}<br />
[ -z ${NUM_QUERY_TYPE_X25} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.X25 -o ${NUM_QUERY_TYPE_X25}<br />
[ -z ${NUM_QUERY_TYPE_NAPTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.naptr -o ${NUM_QUERY_TYPE_NAPTR}<br />
[ -z ${NUM_QUERY_TYPE_DS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ds -o ${NUM_QUERY_TYPE_DS}<br />
[ -z ${NUM_QUERY_TYPE_DNSKEY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.dnskey -o ${NUM_QUERY_TYPE_DNSKEY}<br />
[ -z ${NUM_QUERY_TYPE_TLSA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.tlsa -o ${NUM_QUERY_TYPE_TLSA}<br />
[ -z ${NUM_QUERY_TYPE_SVCB} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.svcb -o ${NUM_QUERY_TYPE_SVCB}<br />
[ -z ${NUM_QUERY_TYPE_SPF} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.spf -o ${NUM_QUERY_TYPE_SPF}<br />
[ -z ${NUM_QUERY_TYPE_ANY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.any -o ${NUM_QUERY_TYPE_ANY}<br />
[ -z ${NUM_QUERY_TYPE_OTHER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.other -o ${NUM_QUERY_TYPE_OTHER}<br />
<br />
[ -z ${NUM_ANSWER_RCODE_NOERROR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NOERROR -o ${NUM_ANSWER_RCODE_NOERROR}<br />
[ -z ${NUM_ANSWER_RCODE_NXDOMAIN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NXDOMAIN -o ${NUM_ANSWER_RCODE_NXDOMAIN}<br />
[ -z ${NUM_ANSWER_RCODE_SERVFAIL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.SERVFAIL -o ${NUM_ANSWER_RCODE_SERVFAIL}<br />
[ -z ${NUM_ANSWER_RCODE_REFUSED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.REFUSED -o ${NUM_ANSWER_RCODE_REFUSED}<br />
[ -z ${NUM_ANSWER_RCODE_nodata} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.nodata -o ${NUM_ANSWER_RCODE_nodata}<br />
[ -z ${NUM_ANSWER_secure} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.secure -o ${NUM_ANSWER_secure}<br />
No Zabbix será registrado dados como esses abaixo e posteriormente pode ser montado um Grafana com eles:<br />
[[Arquivo:Zabbix dns01.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns02.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns03.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns04.png|nenhum|commoldura]]<br />
<br />
== Balanceando o processamento e mantendo a hora certa ==<br />
Vamos instalar 2 programas agora, o IRQBalance e o Chrony. O primeiro para balancear a carga entre os cores e o segundo para manter a data e hora certas no sistema:<br />
# apt install irqbalance<br />
# systemctl enable irqbalance<br />
# apt install chrony<br />
Após a instalação do Chrony edite o arquivo /etc/chrony/chrony.conf, comente e a linha abaixo e adicione seus servidores NTP. Caso não tenha servidores NTP, estou colocando os do NIC.br aqui.<br />
#pool 2.debian.pool.ntp.org iburst<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
<br />
# systemctl restart chronyd.service<br />
Cheque com o '''chronyc''' se os servidores estão OK:<br />
# chronyc sourcestats<br />
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev<br />
==============================================================================<br />
a.st1.ntp.br 10 5 155m -0.027 0.030 -71us 51us<br />
b.st1.ntp.br 11 7 344m +0.068 0.079 +23ms 382us<br />
c.st1.ntp.br 6 3 344m +0.026 0.037 -124us 92us<br />
200.20.186.76 9 3 138m -0.022 0.031 +172us 42us<br />
<br />
# chronyc sources<br />
MS Name/IP address Stratum Poll Reach LastRx Last sample<br />
===============================================================================<br />
^* a.st1.ntp.br 1 10 377 588 +487us[ +397us] +/- 12ms<br />
^- b.st1.ntp.br 2 10 377 830 +23ms[ +23ms] +/- 49ms<br />
^+ c.st1.ntp.br 2 10 21 1038 -147us[ -242us] +/- 17ms<br />
^+ 200.20.186.76 1 10 377 1032 +381us[ +285us] +/- 15ms<br />
<br />
== Configurando o FRRouting ==<br />
Nesse ponto iremos configurar o '''FRRouting''' para enviar os IPs das '''loopbacks''' e o '''/30''' para o nosso PE do diagrama. Em '''/etc/frr/daemons''' habilite o parâmetro conforme abaixo:<br />
ospfd=yes<br />
Edite o arquivo '''/etc/frr/frr.conf''' e deixe com o conteúdo abaixo, para ficar conforme nosso diagrama do projeto. Apenas troque '''<SENHA>''' por uma senha para fechar o OSPF com mais segurança. Essa senha deve ser usada dos dois lados.<br />
frr version 8.2.2<br />
frr defaults traditional<br />
hostname dns-recursivo-01<br />
log syslog informational<br />
no ip forwarding<br />
no ipv6 forwarding<br />
service integrated-vtysh-config<br />
!<br />
interface ens18<br />
ip ospf message-digest-key 5 md5 '''<SENHA>'''<br />
ip ospf network point-to-point<br />
exit<br />
!<br />
router ospf<br />
ospf router-id 172.16.0.6<br />
network 10.10.10.10/32 area 0.0.0.0<br />
network 10.10.9.9/32 area 0.0.0.0<br />
network 172.16.0.4/30 area 0.0.0.0<br />
area 0 authentication message-digest<br />
exit<br />
!<br />
<br />
# systemctl restart frr.service<br />
Cheque se está tudo OK com o OSPF e verifique no PE se está recebendo os prefixos anunciados.<br />
# vtysh -c 'show ip ospf neighbor'<br />
<br />
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL<br />
172.16.0.5 1 Full/- 10m49s 35.310s 172.16.0.5 ens18:172.16.0.6 0 0 0<br />
<br />
# vtysh -c 'show ip ospf neighbor detail'<br />
<br />
Neighbor 172.16.0.5, interface address 172.16.0.5<br />
In the area 0.0.0.0 via interface ens18<br />
Neighbor priority is 1, State is Full/-, 5 state changes<br />
Most recent state change statistics:<br />
Progressive change 21w3d15h ago<br />
DR is 0.0.0.0, BDR is 0.0.0.0<br />
Options 18 *|-|-|EA|-|-|E|-<br />
Dead timer due in 34.685s<br />
Database Summary List 0<br />
Link State Request List 0<br />
Link State Retransmission List 0<br />
Thread Inactivity Timer on<br />
Thread Database Description Retransmision off<br />
Thread Link State Request Retransmission on<br />
Thread Link State Update Retransmission on<br />
<br />
Graceful restart Helper info:<br />
Graceful Restart HELPER Status : None<br />
<br />
== Configurando o Unbound ==<br />
Abaixo a configuração que usaremos nos servidores atentando para o detalhe do '''num-threads''', esse deve ter o valor igual ao número de CPUs do servidor.<br />
<br />
Também os IPs utilizados em '''outgoing-interface''' que serão diferentes em cada servidor, esses serão os IPs usados para '''recursividade'''. Consulte o manual do Unbound para obter mais informações sobre cada parâmetro listado na configuração.<br />
<br />
O tuning no Unbound pode ser alterado conforme abaixo:<br />
num-threads = nº CPUs<br />
so-reuseport = yes<br />
*-slabs = potência de 2 próximo ao num-threads<br />
msg-cache-size = 1G (quantidade de memória pra usar de cache)<br />
rrset-cache-size = 2 * msg-cache-size<br />
outgoing-range = 8192<br />
num-queries-per-thread = 4096<br />
so-rcvbuf e so-sndbuf = 4m ou 8m para servidores com muita requisição<br />
Agora vamos criar nosso arquivo de configuração base em '''/etc/unbound/unbound.conf.d/local.conf''':<br />
server:<br />
verbosity: 1<br />
statistics-interval: 0<br />
statistics-cumulative: no<br />
extended-statistics: yes<br />
num-threads: 6<br />
serve-expired: yes<br />
interface: 127.0.0.1<br />
interface: 10.10.10.10<br />
interface: 10.10.9.9<br />
interface: 172.16.0.6<br />
interface: ::1<br />
interface-automatic: no<br />
outgoing-interface: 198.18.1.10<br />
outgoing-interface: 2001:db8::faca:198:18:1:10<br />
outgoing-range: 8192<br />
outgoing-num-tcp: 1024<br />
incoming-num-tcp: 2048<br />
so-rcvbuf: 4m<br />
so-sndbuf: 4m<br />
so-reuseport: yes<br />
edns-buffer-size: 1232<br />
msg-cache-size: 1G<br />
msg-cache-slabs: 4<br />
num-queries-per-thread: 4096<br />
rrset-cache-size: 2G<br />
rrset-cache-slabs: 4<br />
infra-cache-slabs: 4<br />
do-ip4: yes<br />
do-ip6: yes<br />
do-udp: yes<br />
do-tcp: yes<br />
chroot: ""<br />
username: "unbound"<br />
directory: "/etc/unbound"<br />
logfile: "/var/log/unbound/unbound.log"<br />
use-syslog: no<br />
log-time-ascii: yes<br />
log-queries: no<br />
pidfile: "/var/run/unbound.pid"<br />
root-hints: "/usr/share/dns/root.hints"<br />
hide-identity: yes<br />
hide-version: yes<br />
unwanted-reply-threshold: 10000000<br />
prefetch: yes<br />
prefetch-key: yes<br />
rrset-roundrobin: yes<br />
minimal-responses: yes<br />
module-config: "respip validator iterator"<br />
val-clean-additional: yes<br />
val-log-level: 1<br />
key-cache-slabs: 4<br />
deny-any: yes<br />
access-control: 198.18.0.0/22 allow<br />
access-control: 2001:db8::/32 allow<br />
<br />
rpz:<br />
name: rpz.block.host.local.zone<br />
zonefile: /etc/unbound/rpz.block.hosts.zone<br />
rpz-action-override: nxdomain<br />
<br />
python:<br />
<br />
auth-zone:<br />
name: "."<br />
master: "b.root-servers.net"<br />
master: "c.root-servers.net"<br />
master: "d.root-servers.net"<br />
master: "f.root-servers.net"<br />
master: "g.root-servers.net"<br />
master: "k.root-servers.net"<br />
master: "lax.xfr.dns.icann.org"<br />
master: "iad.xfr.dns.icann.org"<br />
fallback-enabled: yes<br />
for-downstream: no<br />
for-upstream: yes<br />
zonefile: "/var/lib/unbound/root.zone"<br />
No parâmetro '''interface''' colocamos os IPs que serão usados para consulta dos clientes como o '''10.10.10.10''' e o '''10.10.9.9'''. Ali repare que coloquei também o IP privado '''172.16.0.6''', isso porque cada servidor terá o seu IP privado e este deve ser usado pelo seu sistema de monitoramento para checar cada servidor. No '''outgoing-interface''' teremos os IPs, tanto '''IPv4''' quanto '''IPv6''', para que seja feita a recursividade na Internet utilizando eles. Não tem '''IPv6''' ainda na sua rede? Dê uma olhada nesse artigo. Outro parâmetro importante é o '''access-control''' e é através dele que liberamos os prefixos IP para consultarem no nosso DNS Recursivo. No exemplo estou liberando todo o prefixo '''198.18.0.0/22''' e o prefixo '''2001:db8::/32'''. Além da ACL no Unbound, recomendo que crie um filtro de pacotes com iptables ou nftables protegendo seu sistema e liberando as portas '''53/UDP''', '''53/TCP''' e '''443/TCP''' apenas para seus clientes. Falarei sobre a '''443/TCP''' mais para frente nessa mesma documentação.<br />
<br />
Agora criaremos o arquivo '''RPZ''' ('''Response Policy Zones'''). Esse arquivo contém os sites que serão bloqueados via '''<abbr>DNS</abbr> Recursivo'''. São aqueles sites que às vezes você recebe um Ofício da Justiça solicitando o bloqueio deles. Não entrarei no mérito da efetividade desses bloqueios, porque muitos de vocês sabem que tecnicamente, existem formas de se fazer um bypass através desses bloqueios. Contudo vamos deixar nosso ambiente preparado para esses bloqueios e por isso crie o arquivo '''/etc/unbound/rpz.block.hosts.zone''' com esse conteúdo de exemplo:<br />
$TTL 2h<br />
@ IN SOA localhost. root.localhost. (2 6h 1h 1w 2h)<br />
IN NS localhost.<br />
; RPZ manual block hosts<br />
*.josedascoves.com CNAME .<br />
josedascoves.com CNAME .<br />
No exemplo acima estamos bloqueando qualquer consulta de DNS para '''josedascoves.com''' ou qualquer coisa '''.josedascoves.com'''.<br />
<br />
Para testar podemos fazer assim do próprio servidor:<br />
# host josedascoves.com ::1<br />
Using domain server:<br />
Name: ::1<br />
Address: ::1#53<br />
Aliases:<br />
<br />
Host josedascoves.com not found: 3(NXDOMAIN)<br />
Se a resposta for '''NXDOMAIN''' então está funcionando o bloqueio. Para incluir novos bloqueios basta adicionar os domínios, um abaixo do outro, conforme o exemplo que coloquei no arquivo RPZ.<br />
<br />
== Acertando o resolv.conf ==<br />
Vamos modificar nosso /etc/resolv.conf para utilizar DNS externo. Sim você deve estar se perguntando em qual situação isso seria utilizado. Primeiro entenda que o Unbound não irá utilizar o DNS externo para fazer as consultas na Internet e sim, qualquer teste que você faça do servidor precisará apontar para o Unbound usando os IPs '''127.0.0.1''' ou '''::1'''. Faremos isso pela seguinte situação: imagine que o daemon unbound morreu mas você ainda continua com conectividade na Internet. Você conseguiria acessar qualquer local na Internet através do IP mas não através do hostname porque não conseguiria resolver nomes, seu unbound estaria fora do ar. Imagine ainda que você gostaria que seu servidor te avisasse do problema via Telegram ou e-mail. Por isso estamos utilizando um DNS externo no '''/etc/resolv.conf''', apenas para essas situações. Se você não quiser utilizar desse recurso, pode usar o '''127.0.0.1''' e '''::1''' no lugar.<br />
nameserver 8.8.8.8<br />
nameserver 8.8.4.4<br />
nameserver 2001:4860:4860::8888<br />
<br />
== Script de teste de recursividade ==<br />
Estamos montando uma '''Rede de DNS Recursivo Anycast''', então é muito importante que você monitore essa rede para saber se algum node morreu e iniciar o troubleshooting, resolver o problema e levantar o sistema novamente. Tudo isso é importante mas o cliente não deve ficar esperando até você resolver o problema, seu sistema precisa ser inteligente o suficiente para se remover da Rede quando tiver um problema e se inserir novamente, quando o problema estiver sido solucionado. Se você montar uma Rede de DNS e um dos nodes apresentar algum problema, todos os clientes atendidos por aquele node migrarão automaticamente e transparentemente para outro '''DNS Recursivo Anycast''' mais próximo. Isso se chama '''disponibilidade'''.<br />
<br />
O script '''/root/scripts/checa_dns.sh''' abaixo tem a função de fazer os testes de recursividade e checar se o daemon do unbound continua rodando. Se algo acontecer, ele para o anúncio do '''10.10.10.10''' e '''10.10.9.9''' e retorna eles quando tudo estiver resolvido.<br />
# mkdir /root/scripts<br />
<br />
#!/usr/bin/env bash<br />
#Script para teste de recursividade v2.2<br />
# Por Marcelo Gondim<br />
# Em 24-12-2022<br />
#<br />
# checa_dns.sh is free software; you can redistribute it and/or modify<br />
# it under the terms of the GNU General Public License as published by<br />
# the Free Software Foundation; either version 2 of the License, or<br />
# (at your option) any later version.<br />
#<br />
# This program is distributed in the hope that it will be useful,<br />
# but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
# GNU General Public License for more details.<br />
#<br />
# You should have received a copy of the GNU General Public License<br />
# along with this program; if not, write to the Free Software<br />
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
#-----------------------------------------------------------------------<br />
#Informe um domínio por linha:<br />
dominios_testar=(<br />
www.google.com<br />
www.terra.com.br<br />
www.uol.com.br<br />
www.globo.com<br />
www.facebook.com<br />
www.youtube.com<br />
www.twitch.com<br />
www.discord.com<br />
www.debian.org<br />
www.redhat.com<br />
)<br />
corte_taxa_falha=100 #Porcentagem de falha para executar uma ação<br />
#-----------------------------------------------------------------------<br />
remove_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" != "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME morreu!" | /usr/local/sbin/telegram-notify --error --text -<br />
fi<br />
}<br />
<br />
adiciona_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" == "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME retornou do inferno!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
}<br />
<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
#echo "Servidor $HOSTNAME morreu DNS mas tentando levantar!" | /usr/local/sbin/telegram-notify --error --text -<br />
systemctl restart unbound<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
#echo "Servidor $HOSTNAME servico DNS voltou mas tinha morrido!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
<br />
qt_falhas=0<br />
qt_total="${#dominios_testar[@]}"<br />
echo "total_dominios: $qt_total"<br />
for site in "${dominios_testar[@]}"<br />
do<br />
resolver="127.0.0.1"<br />
echo -e " - dominio $site - $resolver - \c"<br />
host $site $resolver &> /dev/null<br />
if [ $? -ne 0 ]; then<br />
((qt_falhas++))<br />
echo -e "[Falhou]"<br />
else<br />
echo -e "[OK]"<br />
fi<br />
done<br />
<br />
taxa_falha=$((qt_falhas*100/qt_total))<br />
echo "Falhas $qt_falhas/$qt_total ($taxa_falha%)"<br />
<br />
if [ "$taxa_falha" -ge "$corte_taxa_falha" ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
adiciona_ospf<br />
Se rodarmos o script manualmente veremos isto:<br />
# /root/scripts/checa_dns.sh<br />
total_dominios: 10<br />
- dominio www.google.com - 127.0.0.1 - [OK]<br />
- dominio www.terra.com.br - 127.0.0.1 - [OK]<br />
- dominio www.uol.com.br - 127.0.0.1 - [OK]<br />
- dominio www.globo.com - 127.0.0.1 - [OK]<br />
- dominio www.facebook.com - 127.0.0.1 - [OK]<br />
- dominio www.youtube.com - 127.0.0.1 - [OK]<br />
- dominio www.twitch.com - 127.0.0.1 - [OK]<br />
- dominio www.discord.com - 127.0.0.1 - [OK]<br />
- dominio www.debian.org - 127.0.0.1 - [OK]<br />
- dominio www.redhat.com - 127.0.0.1 - [OK]<br />
Falhas 0/10 (0%)<br />
Se acontecer 100% de falhas o script irá remover os anúncios do OSPF. Se o daemon do unbound morrer, ele tentará reiniciá-lo. Se tudo normalizar o script irá retornar os anúncios para o OSPF. Deixei comentado no script as partes que enviariam uma notificação para o Telegram. Existem diversas documentações sobre isso na Internet, eu mesmo tenho uma. Assim que eu publicar aqui, atualizo essa documentação e sinta-se à vontade de modificar como desejar.<br />
# chmod 700 /root/scripts/checa_dns.sh<br />
Adicione a linha abaixo em seu '''/etc/crontab''':<br />
*/1 * * * * root /root/scripts/checa_dns.sh<br />
<br />
== Habilitando o DoH (<abbr>DNS</abbr> over HTTPS) - opcional ==<br />
Para habilitar o '''DoH''' no Unbound é bem simples mas só é possível com a versão '''1.17.1''' que vem no '''bullseye-backports''' e que virá na próxima versão do '''Debian 12 (Bookworm)'''. O recurso do '''DoH''' vem para trazer mais segurança e privacidade para o usuário. É um recurso muito pouco utilizado ainda mas que seu cliente pode vir a pedir algum dia.<br />
<br />
Você precisará gerar certificados SSL legítimos e para isso você poderá usar o '''Let's Encrypt''' só que de uma forma não tão convencional.<br />
<br />
Na sequência vamos instalar o Let's Encrypt para gerarmos nosso certificado SSL:<br />
# apt install letsencrypt<br />
Escolha um '''hostname''' para ser usado no nosso '''DoH''' e aponte ele no seu DNS Autoritativo para seus IPs 10.10.10.10 e 10.10.9.9. Aqui vamos usar o seguinte como exemplo: '''doh.brasilpeeringforum.org'''. Para gerarmos nosso certificado iremos usar o tipo '''DNS-01''', ele não necessita que tenhamos um servidor web rodando no servidor e nem tão pouco levanta um serviço na porta 80 para checar o hostname. Ele utiliza o DNS como validador e vai te solicitar que crie um registro '''CNAME''' no seu '''DNS Autoritativo''' para provar que você tem o controle sobre aquele hostname. Antes disso vamos instalar um programa em Python para podermos automatizar nossa renovação de certificado no futuro. Esse programa se encontra '''[https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py aqui]''' mas vou deixá-lo abaixo já modificado o interpretador.<br />
<br />
Crie o arquivo '''/etc/letsencrypt/acme-dns-auth.py''' com o conteúdo abaixo:<br />
#!/usr/bin/env python3<br />
import json<br />
import os<br />
import requests<br />
import sys<br />
<br />
### EDIT THESE: Configuration values ###<br />
<br />
# URL to acme-dns instance<br />
ACMEDNS_URL = "<nowiki>https://auth.acme-dns.io</nowiki>"<br />
# Path for acme-dns credential storage<br />
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"<br />
# Whitelist for address ranges to allow the updates from<br />
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]<br />
ALLOW_FROM = []<br />
# Force re-registration. Overwrites the already existing acme-dns accounts.<br />
FORCE_REGISTER = False<br />
<br />
### DO NOT EDIT BELOW THIS POINT ###<br />
### HERE BE DRAGONS ###<br />
<br />
DOMAIN = os.environ["CERTBOT_DOMAIN"]<br />
if DOMAIN.startswith("*."):<br />
DOMAIN = DOMAIN[2:]<br />
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN<br />
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]<br />
<br />
<br />
class AcmeDnsClient(object):<br />
"""<br />
Handles the communication with ACME-DNS API<br />
"""<br />
<br />
def __init__(self, acmedns_url):<br />
self.acmedns_url = acmedns_url<br />
<br />
def register_account(self, allowfrom):<br />
"""Registers a new ACME-DNS account"""<br />
<br />
if allowfrom:<br />
# Include whitelisted networks to the registration call<br />
reg_data = {"allowfrom": allowfrom}<br />
res = requests.post(self.acmedns_url+"/register",<br />
data=json.dumps(reg_data))<br />
else:<br />
res = requests.post(self.acmedns_url+"/register")<br />
if res.status_code == 201:<br />
# The request was successful<br />
return res.json()<br />
else:<br />
# Encountered an error<br />
msg = ("Encountered an error while trying to register a new acme-dns "<br />
"account. HTTP status {}, Response body: {}")<br />
print(msg.format(res.status_code, res.text))<br />
sys.exit(1)<br />
<br />
def update_txt_record(self, account, txt):<br />
"""Updates the TXT challenge record to ACME-DNS subdomain."""<br />
update = {"subdomain": account['subdomain'], "txt": txt}<br />
headers = {"X-Api-User": account['username'],<br />
"X-Api-Key": account['password'],<br />
"Content-Type": "application/json"}<br />
res = requests.post(self.acmedns_url+"/update",<br />
headers=headers,<br />
data=json.dumps(update))<br />
if res.status_code == 200:<br />
# Successful update<br />
return<br />
else:<br />
msg = ("Encountered an error while trying to update TXT record in "<br />
"acme-dns. \n"<br />
"------- Request headers:\n{}\n"<br />
"------- Request body:\n{}\n"<br />
"------- Response HTTP status: {}\n"<br />
"------- Response body: {}")<br />
s_headers = json.dumps(headers, indent=2, sort_keys=True)<br />
s_update = json.dumps(update, indent=2, sort_keys=True)<br />
s_body = json.dumps(res.json(), indent=2, sort_keys=True)<br />
print(msg.format(s_headers, s_update, res.status_code, s_body))<br />
sys.exit(1)<br />
<br />
class Storage(object):<br />
def __init__(self, storagepath):<br />
self.storagepath = storagepath<br />
self._data = self.load()<br />
<br />
def load(self):<br />
"""Reads the storage content from the disk to a dict structure"""<br />
data = dict()<br />
filedata = ""<br />
try:<br />
with open(self.storagepath, 'r') as fh:<br />
filedata = fh.read()<br />
except IOError as e:<br />
if os.path.isfile(self.storagepath):<br />
# Only error out if file exists, but cannot be read<br />
print("ERROR: Storage file exists but cannot be read")<br />
sys.exit(1)<br />
try:<br />
data = json.loads(filedata)<br />
except ValueError:<br />
if len(filedata) > 0:<br />
# Storage file is corrupted<br />
print("ERROR: Storage JSON is corrupted")<br />
sys.exit(1)<br />
return data<br />
<br />
def save(self):<br />
"""Saves the storage content to disk"""<br />
serialized = json.dumps(self._data)<br />
try:<br />
with os.fdopen(os.open(self.storagepath,<br />
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:<br />
fh.truncate()<br />
fh.write(serialized)<br />
except IOError as e:<br />
print("ERROR: Could not write storage file.")<br />
sys.exit(1)<br />
<br />
def put(self, key, value):<br />
"""Puts the configuration value to storage and sanitize it"""<br />
# If wildcard domain, remove the wildcard part as this will use the<br />
# same validation record name as the base domain<br />
if key.startswith("*."):<br />
key = key[2:]<br />
self._data[key] = value<br />
<br />
def fetch(self, key):<br />
"""Gets configuration value from storage"""<br />
try:<br />
return self._data[key]<br />
except KeyError:<br />
return None<br />
<br />
if __name__ == "__main__":<br />
# Init<br />
client = AcmeDnsClient(ACMEDNS_URL)<br />
storage = Storage(STORAGE_PATH)<br />
<br />
# Check if an account already exists in storage<br />
account = storage.fetch(DOMAIN)<br />
if FORCE_REGISTER or not account:<br />
# Create and save the new account<br />
account = client.register_account(ALLOW_FROM)<br />
storage.put(DOMAIN, account)<br />
storage.save()<br />
<br />
# Display the notification for the user to update the main zone<br />
msg = "Please add the following CNAME record to your main DNS zone:\n{}"<br />
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])<br />
print(msg.format(cname))<br />
<br />
# Update the TXT record in acme-dns instance<br />
client.update_txt_record(account, VALIDATION_TOKEN)<br />
<br />
# chmod +x /etc/letsencrypt/acme-dns-auth.py<br />
Usaremos a seguinte instrução para criar nosso certificado:<br />
# certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d doh.brasilpeeringforum.org<br />
Saving debug log to /var/log/letsencrypt/letsencrypt.log<br />
Plugins selected: Authenticator manual, Installer None<br />
Cert is due for renewal, auto-renewing...<br />
Renewing an existing certificate for doh.brasilpeeringforum.org<br />
Performing the following challenges:<br />
dns-01 challenge for doh.brasilpeeringforum.org<br />
Running manual-auth-hook command: /etc/letsencrypt/acme-dns-auth.py<br />
Output from manual-auth-hook command acme-dns-auth.py:<br />
Please add the following CNAME record to your main DNS zone:<br />
_acme-challenge.doh.brasilpeeringforum.org CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
<br />
Waiting for verification...<br />
<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Challenges loaded. Press continue to submit to CA. Pass "-v" for more info about<br />
challenges.<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Press Enter to Continue<br />
Nesse momento você cria o registro '''CNAME''' no seu DNS Autoritativo conforme ele solicitou: '''_acme-challenge.doh.brasilpeeringforum.org IN CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.''' e somente depois de criado e checado no DNS, você pressiona o '''Enter''' para continuar. Você pode checar dessa forma:<br />
# host -t cname _acme-challenge.doh.brasilpeeringforum.org<br />
_acme-challenge.doh.brasilpeeringforum.org is an alias for b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
Para que nosso certificado seja automaticamente renovado colocaremos no '''/etc/crontab''' a seguinte linha abaixo:<br />
00 00 1 * * root /usr/bin/certbot -q renew<br />
Acima temos a instrução para renovação automática do certificado. Repare que você vai precisar também copiar esse certificado para seus outros servidores, escolha um servidor para manter o certificado sempre atualizado e crie um script que faça a mesma cópia remotamente para os outros servidores. O '''scp''' e o '''rsync''' são seus aliados nisso.<br />
<br />
=== Configurando o Unbound ===<br />
Em nosso '''/etc/unbound/unbound.conf.d/local.conf''', adicionaremos no bloco "'''server:'''" o seguinte:<br />
interface: 10.10.10.10@443<br />
interface: 10.10.9.9@443<br />
tls-service-key: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/privkey.pem"<br />
tls-service-pem: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/fullchain.pem"<br />
Para usar o recurso do '''DoH''' você precisará habilitar o recurso no seu navegador e informar a URL. Vou colocar o exemplo do '''Google Chrome''': Digite '''chrome://settings/security?search=dns''' no seu Chrome e ative '''Usar DNS seguro''', selecione '''Personalizado''' e adicione nossa URL:<br />
[[Arquivo:Doh bpf2.png|nenhum|commoldura]]<br />
<br />
== Finalizando ==<br />
Aqui finalizamos nosso projeto para uma Rede de DNS(s) Recursivos Anycast com Hyperlocal. Esse projeto é escalável, seguro, resiliente e você entregará muito mais qualidade de Internet para o seu cliente. Pare de entregar o '''8.8.8.8''' para os seus clientes, você está contribuindo para uma Internet mais lenta, sem a qualidade que o seu cliente merece. Investi meu tempo, que é muito pouco, para deixar esse documento para a comunidade, para você melhorar o seu ISP, para dar um UP! nele, então vamos começar 2023 com o pé direito. O que acha?<br />
<br />
Como prova de conceito, uma imagem abaixo onde temos uma Rede em produção de DNS(s) Recursivos Anycast e apontando exatamente o momento em que houve alguma situação que fez com que as queries de DNS, convergissem de um node para outro, de forma transparente e automática para o cliente. Podemos notar também que ao ser resolvido o problema, o tráfego retornou para o seu node correto:<br />
[[Arquivo:Convergencia.png|nenhum|commoldura]]<br />
<br />
== KINDNS (Stands for Knowledge-Sharing and Instantiating Norms for DNS and Naming Security) ==<br />
Achou que havia terminado? Agora que você tem a capacidade de montar uma '''Rede de DNS Recursivo''' com todas essas features acima, com todas as ferramentas que foram comentadas, o que acha de certificar o que fez?<br />
<br />
Assim como o [https://www.manrs.org/ MANRS] veio para certificar nosso sistema de roteamento na Internet, agora temos o [https://kindns.org/ KINDNS] para certificar que nossos sistemas de DNS estão bem feitos e dentro dos padrões de segurança. Existem '''7 ações''' que podem ser certificadas para nossos DNS Recursivos e estão aqui em https://kindns.org/shared-private-resolvers/. Com essa nossa documentação, se bem aplicada, você pode se candidatar ao KINDNS e ter seu ASN listado aqui https://kindns.org/participants/<br />
<br />
Obter e manter o '''MANRS''' e '''KINDNS''' demonstra seu compromisso com as Boas Práticas, contribui para termos uma '''Internet''' mais segura e te abre portas para novos negócios que possam exigir essas conformidades.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]<br />
__FORCARTDC__</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=CGNAT_na_pratica&diff=3662CGNAT na pratica2023-12-11T02:55:59Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:773px-Cgnat nic br.jpg|nenhum|miniaturadaimagem|773x773px]]<br />
<br />
==Objetivo==<br />
Com o esgotamento do IPv4 mundialmente, precisamos tomar algumas providências para que a Internet não pare. As que vejo de imediatas são: '''IPv6''' e '''CGNAT (Carrier Grade NAT)'''. O '''IPv6''' é a real solução para os problemas de esgotamento e o CGNAT seria a "gambiarra" necessária para continuar com o IPv4 até que a Internet esteja 100% em IPv6. Nesse artigo será explicado como montar uma caixa CGNAT Determinística usando '''GNU/Linux''' e '''Mikrotik RouterOS'''. Esse artigo foi baseado no treinamento da '''Semana de Capacitação''' do '''NIC.br''' e que pode ser encontrado com o título '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT''' [https://semanacap.bcp.nic.br/6-online/ '''aqui'''] como palestra e material de apoio e o vídeo do treinamento no '''Youtube''' '''[https://www.youtube.com/watch?v=1q7J3NkQVSc aqui]'''.<br />
<br />
== Diagrama ==<br />
[[Arquivo:776px-Cgnat diagrama2.png|esquerda|miniaturadaimagem|776x776px]]<br />
No '''BNG''' é configurado uma '''PBR (Policy Based Routing)''' onde apenas IPs do bloco '''100.64.0.0/22''' serão roteados diretamente para a '''caixa CGNAT'''. Qualquer IPv4 público ou IPv6, serão roteados diretamente para a '''Borda'''. Isso evita processamento e tráfego desnecessário na '''caixa CGNAT'''. <br />
<br />
No diagrama ao lado a linha '''amarela''' simboliza o tráfego do bloco '''100.64.0.0/22''' indo para o '''CGNAT'''. A linha '''vermelha''' seria o tráfego já traduzido para um IP da rede '''198.18.0.0/27''' e encaminhado para a '''Borda'''. A linha '''verde''' é o tráfego mais limpo, sem "gambiarras" e o real objetivo que devemos seguir para uma '''Internet''' melhor usando IPv6.<br />
<br />
A '''Borda''' é um equipamento onde podemos inserir algumas regras de filtros de pacotes stateless para filtrar alguns pacotes indesejados como por exemplo: determinados spoofings e BOGONs. Também onde serão feitas ACLs para filtros BGP. '''Ação 1''' e '''2''' do '''MANRS'''.<br />
<br />
O '''Cliente''' nesse diagrama aparece conectado com o '''IPv4''' de CGNAT '''100.64.0.2''' e '''IPv6 2001:0db8:f18:0:a941:6164:1a79:c0f3'''. Todo o acesso IPv4 desse cliente e nesse exemplo, para a Internet, sairá com o IP '''198.18.0.0''' usando as portas entre '''5056''' e '''7071''', conforme mostraremos no script gerador de regras de CGNAT.<br />
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br />
<br />
<br />
<br />
== CGNAT no GNU/Linux ==<br />
<br />
=== Hardware e Sistema que utilizaremos no GNU/Linux ===<br />
* 2x Intel® Xeon® Silver 4215R Processor (3.20 GHz, 11M Cache, 8 núcleos/16 threads). Ambiente NUMA (non-uniform memory access).<br />
* 32Gb de ram.<br />
* 2x SSD 240 Gb RAID1.<br />
* 2x Interfaces de rede Intel XL710-QDA2 (2 portas de 40 Gbps).<br />
* GNU/Linux Debian 11 (Bullseye).<br />
<br />
Vamos configurar um LACP com as duas portas de cada interface, para que possamos ter um backup, caso algum módulo apresente algum problema. Seu ambiente de produção pode ser diferente e por isso precisamos ter alguns cuidados na hora de montarmos o conjunto de hardware e não obtermos surpresas.<br />
<br />
1º Verifique algumas especificações da interface de rede que será usada. Por exemplo a '''Intel XL710-QDA2''':<br />
<br />
* 2 portas de 40 Gbps.<br />
* PCIe 3.0 x8 (8.0 GT/s).<br />
<br />
Com essa informação seu equipamento não poderá possuir slots PCIe inferiores a esta especificação, caso contrário terá problemas de desempenho.<br />
<br />
Você também precisa estar atento para as limitações de barramento por '''versão''' x '''lane''' ('''x1'''):<br />
<br />
* PCIe 1.0/1.1 - 2.5 GT/s - (8b/10b encoding) - 2 Gbps.<br />
* PCIe 2.0/2.1 - 5.0 GT/s - (8b/10b encoding) - 4 Gbps.<br />
* PCIe 3.0/3.1 - 8.0 GT/s - (128b/130b encoding) - ~7,88 Gbps.<br />
* PCIe 4.0 - 16 GT/s - (128b/130b encoding) - ~15,76 Gbps.<br />
<br />
=== Calculando a capacidade ===<br />
Se observarmos a '''XL710-QDA2''' é '''PCIe 3.0 x8 (8 lanes)''' ou seja o barramento irá suportar:<br />
<br />
* '''8.0 GT/s * (128b/130b encoding) * 8 lanes = 63,01 Gbps'''<br />
<br />
O objetivo do '''LACP''' nesse caso, '''não seria alcançar os''' '''80 Gbps de capacidade''' em cada interface, mesmo porque cada barramento das interfaces é '''limitado em 63,01 Gbps''', mas manteremos um '''backup dos 40 Gbps'''. <br />
<br />
Nessa configuração teríamos teoricamente '''63,01 Gbps de entrada e 63,01 Gbps de saída'''. Mas para esse cenário precisaremos fazer uma coisa chamada '''CPU Affinity'''. Nesse caso colocaríamos um processador dedicado para cada interface de rede. É um cenário mais complexo do que com 1 processador apenas, inclusive necessitamos de olhar o datasheet da motherboard e identificar quais slots PCIe são diretamente controlados por qual CPU. Se temos a '''CPU0''' e '''CPU1''', uma interface precisará ficar no slot controlado pela '''CPU0''' e a outra interface no slot controlado pela '''CPU1''' e '''observar a quantidade de lanes no slot para ver se suporta a mesma quantidade de lanes da interface de rede'''.<br />
<br />
Falando um pouco sobre PPS (Packet Per Second) para calcular por exemplo 1 Gbps de tráfego na ethernet, a quantidade de PPS que o sistema precisaria suportar encaminhar teríamos: 1.000.000.000/8/1518 = 82.345 packets per second.<br />
<br />
Existe um comando no GNU/Linux para você saber se o seu equipamento com processadores físicos, conseguirá trabalhar com o '''CPU Affinity''':<br />
# cat /sys/class/net/<interface>/device/numa_node<br />
Se o resultado do comando acima for '''-1''' então esse equipamento não trabalhará com o '''CPU Affinity'''. Isso porque cada interface precisa estar sendo gerenciada por um node específico. Se são 2 processadores então o resultado deveria ser '''0 de CPU0''' ou '''1 de CPU1'''.<br />
<br />
A seguir veremos um exemplo de datasheet da '''motherboard S2600WF''':<br />
[[Arquivo:903px-S2600wf.png|nenhum|miniaturadaimagem|903x903px]]<br />
Se observarmos o datasheet acima veremos que temos o '''PCIe Riser #1''', '''Riser #2''' e '''Riser #3'''. Cada Riser possui slots PCIe que são gerenciados por determinada CPU. Se colocássemos as duas interfaces de rede nos '''slots do Riser #2''' e '''Riser #3''', estaríamos pendurando tudo apenas no '''processador 2'''. Isso foi apenas para mostrar a complexidade de quando usamos um equipamento '''NUMA''' e estamos somente escolhendo o hardware adequado. Ainda não chegamos na configuração do '''CPU Affinity'''.<br />
<br />
Para sabermos quais cores estão relacionados para uma determinada CPU, utilizamos os comandos abaixo:<br />
# cat /sys/devices/system/node/node0/cpulist<br />
0-7<br />
<br />
# cat /sys/devices/system/node/node1/cpulist<br />
8-15<br />
No exemplo acima a '''CPU0''' '''tem os cores de 0 a 7''' e a '''CPU1, os cores de 8 a 15''', ou seja, é um equipamento com '''16 cores'''.<br />
<br />
=== Tuning antes do CPU Affinity ===<br />
Também é importante, para aumento de performance, que seja '''desabilitado na BIOS o HT (Hyper Threading)'''.<br />
<br />
Antes de configurarmos algumas coisas no nosso ambiente, precisaremos instalar uma ferramenta importante para o nosso tuning; vamos instalar o pacote '''ethtool'''. Ele servirá para fazermos alguns ajustes nas nossas interfaces de rede. Alguns fabricantes podem não permitir certas alterações mas com as interfaces da Intel sempre obtive os resultados esperados. <br />
# apt install ethtool<br />
No nosso exemplo acima vimos que o equipamento possui '''16 cores''' sendo que '''8 cores por CPU'''. Então, para esse caso, faremos um ajuste nas interfaces para ficarem preparadas para receberem '''8 cores em cada através das IRQs'''. Usamos o parâmetro '''-l''' do '''ethtool''' para listar o '''Pre-set maximums combined''' da interface e o parâmetro '''-L''' para alterar esse valor. Façamos então a alteração:<br />
# ethtool -L enp5s0f0 combined 8<br />
# ethtool -L enp5s0f1 combined 8<br />
# ethtool -L enp6s0f0 combined 8<br />
# ethtool -L enp6s0f1 combined 8<br />
Com os comandos acima deixamos preparadas as interfaces para aceitarem '''8 cores em cada uma através das IRQs'''. <br />
<br />
Não podemos usar o programa '''irqbalance''' para o '''CPU Affinity''', pois este faz migração de contextos entre os cores e isso é ruim. Como no nosso exemplo estamos usando uma interface Intel, utilizaremos um script da própria Intel para realizar o '''CPU Affinity''' de forma mais fácil. Esse script se chama '''set_irq_affinity''' e vem acompanhado com os fontes do driver da interface. Ex.: '''[https://www.intel.com/content/www/us/en/download/18026/intel-network-adapter-driver-for-pcie-40-gigabit-ethernet-network-connections-under-linux.html Intel Network Adapter]'''<br />
<br />
=== Código do script '''set_irq_affinity''' ===<br />
#!/bin/bash<br />
# SPDX-License-Identifier: BSD-3-Clause<br />
# Copyright (c) 2015 - 2019, Intel Corporation<br />
#<br />
# Affinitize interrupts to cores<br />
#<br />
# typical usage is (as root):<br />
# set_irq_affinity -x local eth1 <eth2> <eth3><br />
# set_irq_affinity -s eth1<br />
#<br />
# to get help:<br />
# set_irq_affinity<br />
<br />
usage()<br />
{<br />
echo<br />
echo "Usage: option -s <interface> to show current settings only"<br />
echo "Usage: $0 [-x|-X] [all|local|remote [<node>]|one <core>|custom|<cores>] <interface> ..."<br />
echo " Options: "<br />
echo " -s Shows current affinity settings"<br />
echo " -x Configure XPS as well as smp_affinity"<br />
echo " -X Disable XPS but set smp_affinity"<br />
echo " [all] is the default value"<br />
echo " [remote [<node>]] can be followed by a specific node number"<br />
echo " Examples:"<br />
echo " $0 -s eth1 # Show settings on eth1"<br />
<br />
echo " $0 all eth1 eth2 # eth1 and eth2 to all cores"<br />
echo " $0 one 2 eth1 # eth1 to core 2 only"<br />
echo " $0 local eth1 # eth1 to local cores only"<br />
echo " $0 remote eth1 # eth1 to remote cores only"<br />
echo " $0 custom eth1 # prompt for eth1 interface"<br />
echo " $0 0-7,16-23 eth0 # eth1 to cores 0-7 and 16-23"<br />
echo<br />
exit 1<br />
}<br />
<br />
usageX()<br />
{<br />
echo "options -x and -X cannot both be specified, pick one"<br />
exit 1<br />
}<br />
<br />
if [ "$1" == "-x" ]; then<br />
XPS_ENA=1<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-s" ]; then<br />
SHOW=1<br />
echo Show affinity settings<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-X" ]; then<br />
if [ -n "$XPS_ENA" ]; then<br />
usageX<br />
fi<br />
XPS_DIS=2<br />
shift<br />
fi<br />
<br />
if [ "$1" == -x ]; then<br />
usageX<br />
fi<br />
<br />
if [ -n "$XPS_ENA" ] && [ -n "$XPS_DIS" ]; then<br />
usageX<br />
fi<br />
<br />
if [ -z "$XPS_ENA" ]; then<br />
XPS_ENA=$XPS_DIS<br />
fi<br />
<br />
SED=`which sed`<br />
if <nowiki>[[ ! -x $SED ]]</nowiki>; then<br />
echo " $0: ERROR: sed not found in path, this script requires sed"<br />
exit 1<br />
fi<br />
<br />
num='^[0-9]+$'<br />
<br />
# search helpers<br />
NOZEROCOMMA="s/^[0,]*//"<br />
# Vars<br />
AFF=$1<br />
shift<br />
<br />
case "$AFF" in<br />
remote) <nowiki>[[ $1 =~ $num ]]</nowiki> && rnode=$1 && shift ;;<br />
one) <nowiki>[[ $1 =~ $num ]]</nowiki> && cnt=$1 && shift ;;<br />
all) ;;<br />
local) ;;<br />
custom) ;;<br />
[0-9]*) ;;<br />
-h|--help) usage ;;<br />
"") usage ;;<br />
*) IFACES=$AFF && AFF=all ;; # Backwards compat mode<br />
esac<br />
<br />
# append the interfaces listed to the string with spaces<br />
while [ "$#" -ne "0" ] ; do<br />
IFACES+=" $1"<br />
shift<br />
done<br />
<br />
# for now the user must specify interfaces<br />
if [ -z "$IFACES" ]; then<br />
usage<br />
exit 2<br />
fi<br />
<br />
notfound()<br />
{<br />
echo $MYIFACE: not found<br />
exit 15<br />
}<br />
<br />
# check the interfaces exist<br />
for MYIFACE in $IFACES; do<br />
grep -q $MYIFACE /proc/net/dev || notfound<br />
done<br />
<br />
# support functions<br />
<br />
build_mask()<br />
{<br />
VEC=$core<br />
if [ $VEC -ge 32 ]<br />
then<br />
MASK_FILL=""<br />
MASK_ZERO="00000000"<br />
let "IDX = $VEC / 32"<br />
for ((i=1; i<=$IDX;i++))<br />
do<br />
MASK_FILL="${MASK_FILL},${MASK_ZERO}"<br />
done<br />
<br />
let "VEC -= 32 * $IDX"<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X%s" $MASK_TMP $MASK_FILL)<br />
else<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X" $MASK_TMP)<br />
fi<br />
}<br />
<br />
show_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
HINT=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/affinity_hint`<br />
printf "ACTUAL %s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf "HINT %s %d %s <- /proc/irq/$IRQ/affinity_hint\n" $IFACE $core $HINT<br />
IRQ_CHECK=`grep '[-,]' /proc/irq/$IRQ/smp_affinity_list`<br />
if [ ! -z $IRQ_CHECK ]; then<br />
printf " WARNING -- SMP_AFFINITY is assigned to multiple cores $IRQ_CHECK\n"<br />
fi<br />
if [ "$SMP_I" != "$HINT" ]; then<br />
printf " WARNING -- SMP_AFFINITY VALUE does not match AFFINITY_HINT \n"<br />
fi<br />
printf "NODE %s %d %s <- /proc/irq/$IRQ/node\n" $IFACE $core `cat /proc/irq/$IRQ/node`<br />
printf "LIST %s %d [%s] <- /proc/irq/$IRQ/smp_affinity_list\n" $IFACE $core `cat /proc/irq/$IRQ/smp_affinity_list`<br />
printf "XPS %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` ]; then<br />
echo "WARNING: xps rxqs not supported on $IFACE"<br />
else<br />
printf "XPSRXQs %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_rxqs\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` $IFACE $((n-1))<br />
fi<br />
printf "TX_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/tx_maxrate\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/tx_maxrate` $IFACE $((n-1))<br />
printf "BQLIMIT %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit` $IFACE $((n-1))<br />
printf "BQL_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_max\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_max` $IFACE $((n-1))<br />
printf "BQL_MIN %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_min\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_min` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` ]; then<br />
echo "WARNING: aRFS is not supported on $IFACE"<br />
else<br />
printf "RPSFCNT %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_flow_cnt\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` $IFACE $((n-1))<br />
fi<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` ]; then<br />
echo "WARNING: rps_cpus is not available on $IFACE"<br />
else<br />
printf "RPSCPU %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` $IFACE $((n-1))<br />
fi<br />
echo<br />
}<br />
<br />
set_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
printf "%s" $MASK > /proc/irq/$IRQ/smp_affinity<br />
printf "%s %d %s -> /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $MASK<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
if [ "$SMP_I" != "$MASK" ]; then<br />
printf " ACTUAL\t%s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf " WARNING -- SMP_AFFINITY setting failed\n"<br />
fi<br />
case "$XPS_ENA" in<br />
1)<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
2)<br />
MASK=0<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
*)<br />
esac<br />
}<br />
<br />
# Allow usage of , or -<br />
#<br />
parse_range () {<br />
RANGE=${@//,/ }<br />
RANGE=${RANGE//-/..}<br />
LIST=""<br />
for r in $RANGE; do<br />
# eval lets us use vars in {#..#} range<br />
<nowiki>[[ $r =~ '..' ]]</nowiki> && r="$(eval echo {$r})"<br />
LIST+=" $r"<br />
done<br />
echo $LIST<br />
}<br />
<br />
# Affinitize interrupts<br />
#<br />
doaff()<br />
{<br />
CORES=$(parse_range $CORES)<br />
ncores=$(echo $CORES | wc -w)<br />
n=1<br />
<br />
# this script only supports interrupt vectors in pairs,<br />
# modification would be required to support a single Tx or Rx queue<br />
# per interrupt vector<br />
<br />
queues="${IFACE}-.*TxRx"<br />
<br />
irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(for i in `ls -1 /sys/class/net/${IFACE}/device/msi_irqs | sort -n` ;do grep -w $i: /proc/interrupts | egrep -v 'fdir|async|misc|ctrl' | cut -f 1 -d :; done)<br />
[ -z "$irqs" ] && echo "Error: Could not find interrupts for $IFACE"<br />
<br />
if [ "$SHOW" == "1" ] ; then<br />
echo "TYPE IFACE CORE MASK -> FILE"<br />
echo "============================"<br />
else<br />
echo "IFACE CORE MASK -> FILE"<br />
echo "======================="<br />
fi<br />
<br />
for IRQ in $irqs; do<br />
[ "$n" -gt "$ncores" ] && n=1<br />
j=1<br />
# much faster than calling cut for each<br />
for i in $CORES; do<br />
[ $((j++)) -ge $n ] && break<br />
done<br />
core=$i<br />
if [ "$SHOW" == "1" ] ; then<br />
show_affinity<br />
else<br />
set_affinity<br />
fi<br />
((n++))<br />
done<br />
}<br />
<br />
# these next 2 lines would allow script to auto-determine interfaces<br />
#[ -z "$IFACES" ] && IFACES=$(ls /sys/class/net)<br />
#[ -z "$IFACES" ] && echo "Error: No interfaces up" && exit 1<br />
<br />
# echo IFACES is $IFACES<br />
<br />
CORES=$(</sys/devices/system/cpu/online)<br />
[ "$CORES" ] || CORES=$(grep ^proc /proc/cpuinfo | cut -f2 -d:)<br />
<br />
# Core list for each node from sysfs<br />
node_dir=/sys/devices/system/node<br />
for i in $(ls -d $node_dir/node*); do<br />
i=${i/*node/}<br />
corelist[$i]=$(<$node_dir/node${i}/cpulist)<br />
done<br />
<br />
for IFACE in $IFACES; do<br />
# echo $IFACE being modified<br />
<br />
dev_dir=/sys/class/net/$IFACE/device<br />
[ -e $dev_dir/numa_node ] && node=$(<$dev_dir/numa_node)<br />
[ "$node" ] && [ "$node" -gt 0 ] || node=0<br />
<br />
case "$AFF" in<br />
local)<br />
CORES=${corelist[$node]}<br />
;;<br />
remote)<br />
[ "$rnode" ] || { [ $node -eq 0 ] && rnode=1 || rnode=0; }<br />
CORES=${corelist[$rnode]}<br />
;;<br />
one)<br />
[ -n "$cnt" ] || cnt=0<br />
CORES=$cnt<br />
;;<br />
all)<br />
CORES=$CORES<br />
;;<br />
custom)<br />
echo -n "Input cores for $IFACE (ex. 0-7,15-23): "<br />
read CORES<br />
;;<br />
[0-9]*)<br />
CORES=$AFF<br />
;;<br />
*)<br />
usage<br />
exit 1<br />
;;<br />
esac<br />
<br />
# call the worker function<br />
doaff<br />
done<br />
<br />
# check for irqbalance running<br />
IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?`<br />
if [ "$IRQBALANCE_ON" == "0" ] ; then<br />
echo " WARNING: irqbalance is running and will"<br />
echo " likely override this script's affinitization."<br />
echo " Please stop the irqbalance service and/or execute"<br />
echo " 'killall irqbalance'"<br />
exit 2<br />
fi<br />
<br />
=== CPU Affinity ===<br />
Agora que preparamos as interfaces, façamos os apontamentos dos cores da seguinte forma. Vamos supor que colocamos o script em '''/root/scripts''':<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
<br />
=== Mais alguns tunings ===<br />
Vamos fazer mais alguns ajustes nas interfaces com o '''ethtool'''. Dessa vez vamos aumentar os '''Rings RX''' e '''TX'''. Mas antes vamos listar os valores que podemos usar:<br />
# ethtool -g enp5s0f0<br />
Ring parameters for enp5s0f0:<br />
Pre-set maximums:<br />
RX: 4096<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 4096<br />
Current hardware settings:<br />
RX: 512<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 512<br />
Acima vemos que o '''valor máximo é de 4096 tanto para TX''', '''quanto para RX''' mas está '''configurado para 512 em RX e TX'''. Façamos então:<br />
# ethtool -G enp5s0f0 rx 4096 tx 4096<br />
# ethtool -G enp5s0f1 rx 4096 tx 4096<br />
# ethtool -G enp6s0f0 rx 4096 tx 4096<br />
# ethtool -G enp6s0f1 rx 4096 tx 4096<br />
Vamos desabilitar as seguintes options das interfaces: '''TSO''', '''GRO''' e '''GSO'''. <br />
# ethtool -K enp5s0f0 tso off gro off gso off<br />
# ethtool -K enp5s0f1 tso off gro off gso off<br />
# ethtool -K enp6s0f0 tso off gro off gso off<br />
# ethtool -K enp6s0f1 tso off gro off gso off<br />
Aumentaremos o '''txqueuelen''' para '''10000''':<br />
# ip link set enp5s0f0 txqueuelen 10000<br />
# ip link set enp5s0f1 txqueuelen 10000<br />
# ip link set enp6s0f0 txqueuelen 10000<br />
# ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Salvando a configuração e criando o LACP ===<br />
Tudo que fizemos até o momento será perdido no próximo reboot do sistema, então faremos com que esses comandos sejam executados sempre que o sistema iniciar. Para isso vamos deixar o nosso arquivo '''/etc/network/interfaces''' configurado conforme nosso diagrama, usando '''LACP''' e executando nossos comandos anteriores.<br />
<br />
Antes precisaremos instalar o pacote '''ifenslave''' para que o bonding funcione:<br />
# apt install ifenslave<br />
# modprobe bonding<br />
# echo "bonding" >> /etc/modules<br />
Abaixo o nosso '''/etc/network/interfaces''' já com todas as configurações que fizemos anteriormente e seguindo nosso diagrama de exemplo:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto bond0<br />
iface bond0 inet static<br />
bond-slaves enp5s0f0 enp5s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 10.0.10.172/24<br />
gateway 10.0.10.1<br />
pre-up /usr/sbin/ethtool -L enp5s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp5s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
pre-up /usr/sbin/ethtool -G enp5s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp5s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp5s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp5s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp5s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp5s0f1 txqueuelen 10000<br />
<br />
auto bond1<br />
iface bond1 inet static<br />
bond-slaves enp6s0f0 enp6s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 192.168.0.1/24<br />
pre-up /usr/sbin/ethtool -L enp6s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp6s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
pre-up /usr/sbin/ethtool -G enp6s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp6s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp6s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp6s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp6s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Atualizando o Kernel ===<br />
Colocaremos o '''kernel do backports'''. Para isso deixe o seu '''/etc/apt/sources''' conforme abaixo e rode os comandos na sequência:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
<br />
# apt update<br />
# apt install -t bullseye-backports linux-image-amd64<br />
# reboot<br />
<br />
=== Protegendo contra static loop e preparando o ambiente do CGNAT ===<br />
O '''static loop''' é algo que, definitivamente, pode derrubar toda a sua operação se não for devidamente tratado e pode ser facilmente explorado por pessoas mal intencionadas. A causa do problema é uma rota estática para um prefixo IP (seja IPv4 ou IPv6), que aponta para um next-hop e nesse destino não existe nenhuma informação sobre o prefixo IP na tabela de rotas local, obrigando o pacote a retornar para o seu gateway default e ficando nesse loop até que '''expire o TTL (Time To Live) do pacote'''. Isso ocorre muito nos casos em que temos concentradores PPPoE (BNG) e caixas CGNAT como esta que estaremos fazendo. Em '''[[Recomendações sobre Mitigação DDoS]]''' temos outras dicas de segurança sobre o assunto '''DDoS'''.<br />
<br />
Crie um arquivo /etc/rc.local e dentro colocaremos algumas coisas como as blackholes para cada prefixo IPv4 público que usaremos no nosso servidor de exemplo e rotas de retorno para o nosso BNG:<br />
# > /etc/rc.local<br />
# chmod +x /etc/rc.local<br />
Dentro teremos:<br />
#!/bin/sh -e<br />
/usr/sbin/ip route add blackhole 198.18.0.0/27 metric 254<br />
/usr/sbin/ip route add 100.64.0.0/22 via 192.168.0.2<br />
No exemplo acima estamos colocando em '''blackhole''' o nosso prefixo IPv4 público deste tutorial que é o '''198.18.0.0/27''' e adicionando uma rota de retorno do prefixo '''100.64.0.0/22''' usado no nosso '''BNG''' para o '''next-hop 192.168.0.2'''.<br />
<br />
=== Redução dos tempos de timeouts e outros ajustes ===<br />
Os tempos padrões dos '''timeouts''' de '''tcp''' e '''udp''' são altos para o nosso sistema de CGNAT, ainda mais quando estamos '''diminuindo a quantidade de portas tcp/udp por assinante''' e com isso podemos rapidamente estourar esse limite, fazendo com que o sistema pare de funcionar. Abaixo estou colocando os valores que sempre usei e não percebi problemas, mas você pode ajustar conforme achar mais prudente. Adicionaremos as configurações abaixo também no nosso '''/etc/rc.local''': <br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_sent<br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_recv<br />
echo 86400 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_fin_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_last_ack<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_max_retrans<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_unacknowledged<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout<br />
echo 180 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_icmp_timeout<br />
echo 600 > /proc/sys/net/netfilter/nf_conntrack_generic_timeout<br />
Em '''/etc/sysctl.conf''' adicionaremos:<br />
net.core.default_qdisc=fq<br />
net.ipv4.tcp_congestion_control=bbr<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.ipv4.conf.all.forwarding=1<br />
net.netfilter.nf_conntrack_helper=1<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
As configurações acima melhoram o uso de memória, habilita o encaminhamento dos pacotes e aumenta a quantidade máxima de '''conntracks''' do sistema para '''4096000'''.<br />
<br />
'''Se o conntrack estourar, seu CGNAT terá problemas e causará indisponibilidades'''. Para consultar a quantidade de conntracks em uso:<br />
# cat /proc/sys/net/netfilter/nf_conntrack_count<br />
Para listar as '''conntracks''':<br />
# cat /proc/net/nf_conntrack<br />
<br />
=== Ajustando a data e horário do sistema ===<br />
Uma tarefa muito importante a ser feita nos servidores, é garantir que o horário e data estejam corretos e para isso usaremos o programa '''chrony'''. Eu prefiro usar sempre horário UTC nos servidores e fazer a conversão quando necessário:<br />
# apt install chrony<br />
Basta copiar e colar os comandos abaixo, para configurar o '''chrony''':<br />
# cat << EOF > /etc/chrony/chrony.conf<br />
confdir /etc/chrony/conf.d<br />
sourcedir /run/chrony-dhcp<br />
sourcedir /etc/chrony/sources.d<br />
keyfile /etc/chrony/chrony.keys<br />
driftfile /var/lib/chrony/chrony.drift<br />
ntsdumpdir /var/lib/chrony<br />
logdir /var/log/chrony<br />
maxupdateskew 100.0<br />
rtcsync<br />
makestep 1 3<br />
leapsectz right/UTC<br />
EOF<br />
<br />
# cat << EOF > /etc/chrony/sources.d/nic.sources<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
EOF<br />
Aqui reiniciamos o serviço e configuramos o '''timezone''':<br />
# systemctl restart chronyd.service<br />
# timedatectl set-timezone "UTC"<br />
<br />
=== Habilitando ALG (Application Layer Gateway) ===<br />
No arquivo '''/etc/modules''' adicionaremos os módulos que usaremos no nosso CGNAT, inclusive os ALGs'''.''' Sem eles alguns serviços, ainda muito utilizados, apresentarão problemas.<br />
<br />
Em '''/etc/modules''' adicionaremos mais os módulos abaixo:<br />
nf_conntrack<br />
nf_nat_pptp<br />
nf_nat_h323<br />
nf_nat_sip<br />
nf_nat_irc<br />
nf_nat_ftp<br />
nf_nat_tftp<br />
<br />
=== Preparando ambiente e gerador de regras de CGNAT ===<br />
Antes de começarmos nossas regras de CGNAT precisaremos de alguns pacotes:<br />
# apt install python3-pip nftables<br />
# pip install ipaddress<br />
Vamos precisar também de um gerador de regras de CGNAT para '''nftables'''. Porque criar as regras manualmente não é uma tarefa rápida e para isso usaremos um programa em python criado por '''José Beiriz''' e disponibilizado aqui: '''[https://github.com/Beiriz/GRCN GRCN]'''<br />
<br />
Caso não consigam baixar por algum motivo o '''GRCN''', aqui '''https://github.com/gondimcodes/GRCN''' também pode ser encontrado o script '''cgnat-nft.py.'''<br />
<br />
Nosso sistema de regras CGNAT será dividido em 2 partes:<br />
<br />
* O script base que colocaremos em '''/root/scripts''' chamado de '''frw-nft.sh'''. Esse script conterá as regras básicas do CGNAT e este incluirá a chamada para os outros arquivos de regras propriamente ditos do CGNAT. <br />
<br />
* Essa outra parte é composta pelos arquivos de regras de CGNAT, onde são feitas as traduções de IPs privados '''100.64.0.0/10 (Shared Address Space - RFC6598)''', para os '''IPs públicos'''. A seguir o '''frw-nft.sh''':<br />
<br />
Nosso script de CGNAT base '''/root/scripts/frw-nft.sh''':<br />
#!/usr/sbin/nft -f<br />
# limpa todas as regras da memoria<br />
flush ruleset<br />
<br />
add table ip filter<br />
add ct helper ip filter pptp-vpn { type "pptp" protocol tcp; }<br />
add ct helper ip filter ftp-padrao { type "ftp" protocol tcp; }<br />
add ct helper ip filter sip-padrao { type "sip" protocol udp; }<br />
add chain ip filter PREROUTING { type filter hook prerouting priority filter; }<br />
add rule ip filter PREROUTING tcp dport 1723 ct helper set "pptp-vpn" <br />
add rule ip filter PREROUTING tcp dport 21 ct helper set "ftp-padrao" <br />
add rule ip filter PREROUTING ip protocol udp ct helper set "sip-padrao"<br />
<br />
# regras base para o CGNAT<br />
add table ip nat<br />
<br />
add chain ip nat POSTROUTING { type nat hook postrouting priority 100; policy accept; }<br />
<br />
add chain ip nat CGNATOUT<br />
<br />
# libera o proprio CGNAT para acessar a Internet - para atualizacoes por exemplo<br />
add rule ip nat POSTROUTING oifname "bond0" ip saddr 10.0.10.172 counter snat to 198.18.0.0 <br />
<br />
# faz o jump para as regras de CGNAT<br />
add rule ip nat POSTROUTING oifname "bond0" counter jump CGNATOUT<br />
<br />
# carrega os arquivos de regras de CGNAT<br />
'''include "/root/scripts/cgnat-0-31.conf"'''<br />
A última linha do script acima, em '''negrito''', é o arquivo de regras CGNAT que iremos gerar e será chamado pelo script quando for executado.<br />
<br />
Após a criação do script, alteramos a permissão dele para ficar como executável e adicionamos ele em nosso '''/etc/rc.local''':<br />
# chmod 700 /root/scripts/frw-nft.sh<br />
# echo "/root/scripts/frw-nft.sh" >> /etc/rc.local<br />
<br />
=== Gerando nossas regras de CGNAT ===<br />
Colocaremos o script '''cgnat-nft.py''' em '''/root/scripts/'''. Como estamos trabalhando '''no modelo determinístico de 1/32''', basta pegarmos nosso bloco privado '''100.64.0.0/22 (1024 IPs)''' e nosso bloco público '''198.18.0.0/27 (32 IPs)''' e executarmos em linha de comando:<br />
# cd /root/scripts<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
Se digitar apenas '''./cgnat-nft.py''' será apresentado um help dos parâmetros mas é bem simples o seu uso. No comando acima '''temos o número 0 como índice'''. Muito cuidado com o índice, porque ele é muito importante para a performance e para cada novo arquivo gerado, esse índice precisará ser incrementado. O comando acima criará automaticamente o arquivo chamado '''cgnat-0-31.conf''', aquele mesmo visto no script base sendo carregado com o '''include'''. Onde esse '''0-31''' quer dizer que nesse arquivo '''os índices vão de 0 a 31'''. Se for gerar um novo arquivo com o comando acima, o próximo índice a ser usado seria o '''32'''. Por exemplo:<br />
# ./cgnat-nft.py '''32''' 198.18.0.32/27 100.64.4.0/22 1/32<br />
Esse comando acima criará novas regras no arquivo chamado '''cgnat-32-63.conf''', na sequência inclua esse novo arquivo dentro do '''/root/scripts/frw-nft.sh''' e '''execute o /root/scripts/frw-nft.sh novamente''' para carregar as novas regras. A seguir daremos uma olhada nas regras geradas nesses arquivos.<br />
<br />
=== Executando o gerador de regras ===<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
[[Arquivo:1022px-Grcn.png|nenhum|miniaturadaimagem|1022x1022px]]<br />
Após teclar '''ENTER''' será gerado o arquivo '''cgnat-0-31.conf''' com as regras conforme a tela abaixo de exemplo:<br />
[[Arquivo:1027px-Regras01.png|nenhum|miniaturadaimagem|1027x1027px]]<br />
Na tela abaixo se observarmos o '''retângulo vermelho''' veremos a regra que faz o '''NAT de tudo que não for TCP ou UDP''' e por fim a regra que faz o '''jump''' '''de tudo que for origem 100.64.0.0/27''' para o '''CGNATOUT_0''' onde esse '''0 é o índice'''.<br />
[[Arquivo:1029px-Regras02.png|nenhum|miniaturadaimagem|1029x1029px]]<br />
<br />
=== Explicando a função dos índices ===<br />
O sistema de avaliação de regras de filtros de pacotes e NAT no GNU/Linux é do tipo '''First Match Win''', o que significa que a pesquisa das regras se encerra quando o sistema encontra uma regra que dê match. O sistema fica muito mais otimizado e performático quando quebramos as regras e separamos em '''CHAINS''' e é aí que entram os '''índices'''. Porque as '''CHAINS''' não podem ter o mesmo nome, senão não haveria separação das regras. A seguir veremos por exemplo que quando houver um pacote relacionado com o prefixo de origem '''100.64.0.0/27''', este será encaminhado para a chain '''CGNATOUT_0''', que é onde estão as regras de CGNAT para esse bloco IP. Desse jeito a checagem para esse prefixo não percorre todas as regras de NAT contidas na memória.<br />
[[Arquivo:Regras03.png|nenhum|miniaturadaimagem|1034x1034px]]<br />
<br />
=== Novo script gerador de regras nftables com suporte a netmap ===<br />
Com a versão nova que virá do '''Debian''', a '''versão 12 (Bookworm)''', teremos também uma versão nova do '''nftables''' '''1.0.6''' e essa versão já suporta o equivalente ao '''netmap''' que temos no '''Mikrotik''' e com isso teremos menos regras na memória e provavelmente mais performance. O sistema novo conta também com o '''kernel 6.1.27''' que possui diversas '''melhorias na pilha tcp/ip'''. Para aqueles que já quiserem testar nesse novo ambiente, fiz uma modificação no script python mostrado anteriormente, para gerar regras nesse novo formato e um arquivo tabela com o relacionamento de portas e IPs para quebra de sigilo tecnológico. Aqui '''https://github.com/gondimcodes/GRCN''' o novo código e estarei solicitando ao '''José Beiriz''' para incorporá-lo no '''GRCN'''.<br />
Para gerar as regras é só executar da mesma maneira. Exemplo:<pre><br />
# ./cgnat-nft-netmap.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
</pre>Abaixo exemplos de como ficam as novas regras e na memória:<br />
[[Arquivo:Nftables netmap1.png|nenhum|commoldura|926x926px]]<br />
[[Arquivo:Nftables netmap2.png|nenhum|commoldura|929x929px]]<br />
Exemplo do arquivo '''tabela-0-31.txt''' que foi gerado:<br />
[[Arquivo:Nftables netmap3.png|nenhum|commoldura|963x963px]]<br />
<br />
=== Simulando um acesso do cliente e observando os resultados ===<br />
Para testar as regras, foi criado um ambiente virtual de laboratório usando um '''Proxmox''' e criando 3 VMs: '''CGNAT''', '''BNG''' e '''CLIENTE'''. Do router de testes capturei os pacotes para demonstrar como funciona o CGNAT. A seguir teremos o acesso por parte do cliente e a captura dos pacotes somente para uma '''POC (Proof of Concept)''', para demonstrar que o CGNAT está funcionando e alocando a porta, dentro do range de portas, corretamente para um determinado cliente.<br />
<br />
Abaixo temos um exemplo de captura bem simples de pacote mostrando que o '''IP 198.18.0.0''' com '''porta origem''' '''6767/TCP''' acessou o '''200.147.41.220 na porta 443/TCP''', um acesso para o site do '''UOL'''.<br />
[[Arquivo:Cgnat sniffer.png|nenhum|miniaturadaimagem|1039x1039px]]<br />
Se olharmos os dados marcados acima e procurarmos pelo IP '''198.18.0.0 e porta 6767''' no nosso arquivo de configuração do CGNAT, '''acharemos o IP 100.64.0.2''' '''que utiliza o range de portas entre 5056 e 7071'''. Abaixo o nosso arquivo de regras de CGNAT para comprovar o range de portas utilizados.<br />
[[Arquivo:Regras5.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Monitorando o tráfego em tempo real ===<br />
Monitorando o '''tráfego Mbps/PPS''' com a ferramenta '''bmon'''. Para instalar o software no Debian basta fazer: <br />
# apt install bmon<br />
Para monitorar as interfaces faríamos algo assim onde '''-b''' para '''bits/s''' e o '''-p''' para '''selecionar as interfaces que quer monitorar'''. Para monitorar nosso '''bond0''' e '''bond1''' o comando seria esse abaixo:<br />
# bmon -b -p bond0,bond1<br />
Abaixo uma tela de exemplo do '''bmon''' em execução:<br />
[[Arquivo:Bmon cgnat.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
== CGNAT no Mikrotik RouterOS ==<br />
Uma boa opção para caixa CGNAT com custo x benefício acessível seria uma '''CCR1036-8G-2S+''' onde se for configurada somente para fazer '''CGNAT''', com o '''mínimo de regras de filtro''' e '''Fasttrack habilitado''', já alcancei '''13 Gbps de tráfego ou 26 Gbps agregado''' fazendo um '''bonding com as 2 interfaces ópticas de 10Gbps'''.<br />
<br />
Essa imagem abaixo foi retirada do '''datasheet da CCR1036-8G-2S+''':<br />
[[Arquivo:Datasheet ccr1036.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Configurando o sistema ===<br />
Instale um '''Mikrotik RouterOS do zero''', procure utilizar a '''versão mais estável possível'''. Como não utilizei ainda em produção o RouterOS 7.x, '''sugiro utilizar a versão 6.48.6 Long-term''', que até o momento, é a versão considerada mais estável. O processo de configurar um '''CGNAT Determinístico no Mikrotik RouterOS''' será bem mais simples que no '''Debian GNU/Linux''' mas a capacidade alcançada com o '''GNU/Linux''' será bem superior ao visto aqui.<br />
<br />
=== Sobre Fasttrack ===<br />
O '''Fasttrack''' é um recurso muito importante que aumentará a performance da sua caixa CGNAT, '''acelerando o encaminhamento de pacotes''' e '''diminuindo o consumo de CPU'''. Neste momento não faremos isso. Quando chegarmos no processo de criação das regras de CGNAT, ele será habilitado e será mostrado quais as regras que fazem isso.<br />
<br />
=== Configurando o bonding ===<br />
Como usaremos as '''duas portas de 10GbE sfp+ da CCR''', utilizaremos vlans para separar a rede que se comunicará com a Internet, da rede com o BNG. A seguir veremos como deixar o nosso bonding. Na sequência configuramos nossas vlans de entrada e saída e em cima delas '''os IPs do diagrama''', como fizemos com o Debian. Vamos definir a '''vlan 101''' '''para a interface que fará a comunicação com a Internet''' e '''por onde será feito o CGNAT''' e a '''vlan 102 que fará a comunicação com o BNG'''.<br />
[[Arquivo:Cgnat mk1.png|nenhum|miniaturadaimagem|1043x1043px]]<br />
[[Arquivo:Cgnat mk2.png|nenhum|miniaturadaimagem|1237x1237px]]<br />
<br />
=== Configurando os IPs e rotas ===<br />
O objetivo deste artigo é ser bem simples para entendermos os conceitos e por isso estamos utilizando '''rotas estáticas''' e não estamos envolvendo outros protocolos como o '''OSPF'''. Nada impediria de utilizar a mesma técnica apresentada aqui em um cenário com '''OSPF''', por exemplo.<br />
<br />
A seguir veremos que na '''vlan-101-borda''' configuramos o '''IP 10.0.10.172/24''' e na '''vlan-102-bng''' configuramos o '''IP 192.168.0.1/24'''.<br />
<br />
Como rotas criamos uma '''default route''' apontando para o '''IP 10.0.10.1''', criamos uma rota para '''100.64.0.0/22''' com '''next-hop 192.168.0.2''' e para nos '''protegermos de static loop''' teremos nossas rotas de '''blackhole''' quando formos gerar as regras de CGNAT.<br />
<br />
Na imagem aparece como '''unreachable''' porque esse equipamento, que está sendo usado como lab, não está conectado em uma switch.<br />
[[Arquivo:Cgnat mk3.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
=== Recomendações de segurança ===<br />
<br />
* Utilize credenciais de acesso '''com senhas fortes''', não esqueça o login admin sem senha (padrão no Mikrotik RouterOS).<br />
<br />
* Desabilite todos os serviços que não for utilizar e os que ficarem abertos, especifique neles o acesso apenas da sua rede de gerência. Não deixe qualquer serviço aberto para a Internet.<br />
<br />
* Habilite o '''TCP SynCookies'''.<br />
<br />
Procure criar suas regras de filtros de pacotes sempre na '''Table Raw''', ela não agride tanto a performance do equipamento mas '''necessita de muita atenção''' porque ela pode afetar os acessos dos assinantes. Isso porque uma regra genérica demais será analisada tanto com destino a caixa, quanto destino ao cliente e o mesmo pode ocorrer no sentido inverso, do cliente para a Internet.<br />
[[Arquivo:Cgnat mk4.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Acertando data e hora ===<br />
Configure o '''NTP client da caixa''' e mantenha a data e horário sincronizados.<br />
[[Arquivo:Cgnat mk5.png|nenhum|miniaturadaimagem|1042x1042px]]<br />
<br />
=== Criando as regras de CGNAT ===<br />
Para simplificar nossa vida, '''Rudimar Remontti''' criou em seu blog, um sistema para gerar regras de '''CGNAT Determinístico''' de forma simples e performática, '''utilizando regras netmap da Mikrotik'''. Para tanto o link é este:<br />
<br />
'''https://cgnat.remontti.com.br/'''<br />
<br />
O sistema é bem completo, simples, irá gerar as '''regras de CGNAT''' e nossas '''blackholes''' para '''bloqueio de static loop'''. Também no final teremos uma '''tabela de associação''' que devemos guardar para fazer as quebras de sigilo solicitadas nos Ofícios Judiciais.<br />
<br />
Ao acessar o site e seguindo o nosso diagrama completaremos as informações conforme mostrado a seguir.<br />
[[Arquivo:Cgnat remontti1.png|nenhum|miniaturadaimagem|1047x1047px]]<br />
O site irá gerar automaticamente os comandos de onde faremos uma cópia e executaremos no nosso equipamento '''Mikrotik RouterOS'''.<br />
[[Arquivo:Cgnat remontti2.png|nenhum|miniaturadaimagem|1049x1049px]]<br />
No final da página é gerado uma tabela do mapeamento das portas, isso deve ser salvo como documento importante pois será usado para quebra de sigilo tecnológico.<br />
[[Arquivo:Cgnat remontti3.png|nenhum|miniaturadaimagem|1052x1052px]]<br />
O conceito é o mesmo, quebrar as regras em blocos menores para chegarmos no nosso '''First Match Win mais rápido''' e não termos que percorrer todas as regras em memória.<br />
[[Arquivo:Cgnat remontti4.png|nenhum|miniaturadaimagem|1053x1053px]]<br />
Abaixo como ficaram as regras que habilita o '''Fasttrack''' no nosso equipamento, aumentando em muito a performance de encaminhamento dos pacotes.<br />
[[Arquivo:Cgnat mk6.png|nenhum|miniaturadaimagem|1056x1056px]]<br />
<br />
== Conclusão ==<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=DNS_Recursivo_Anycast_Hyperlocal&diff=3661DNS Recursivo Anycast Hyperlocal2023-12-05T00:37:10Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
==Introdução==<br />
Você sabe como funciona a Internet? Essa é uma pergunta que meu amigo '''Thiago Ayub''' sempre faz aos seus candidatos à vagas de emprego e não importa o quanto tenham de experiência em '''Engenharia de Redes''', todos sempre travam nesse momento. Todos estão sempre prontos e preparados para resolver os problemas mais cabeludos em '''BGP''', '''OSPF''', '''MPLS''', etc mas travam com essa simples pergunta. Para contextualizar e visualizarmos melhor vamos nos atentar à imagem abaixo e uma explicação simplificada de como funciona:<br />
[[Arquivo:Dns hierarquia.png|esquerda|commoldura]]<br />
Tudo começa com um usuário sentado confortavelmente e querendo acessar um conteúdo disponível na Internet. Ele digita em seu navegador preferido a URL: '''<nowiki>https://wiki.brasilpeeringforum.org</nowiki>''',<br />
<br />
'''<big>1)</big>''' <big>O</big> <big>navegador irá requisitar do '''DNS Recursivo''' utilizado pelo usuário, o '''endereço IP''' que responde pelo nome '''wiki.'''</big>'''brasilpeeringforum.org'''<big>. Isso porque todos os acessos se dão na Internet através do '''endereço''' '''IP''' e não através do '''nome'''. Imaginem se tivéssemos que decorar os endereços IPs de todos os sites e serviços que quiséssemos acessar na Internet?</big> <br />
<br />
<big>'''2)''' Nosso DNS Recursivo checa se a informação consta em seu cache.</big> Se a informação existir ela é devolvida ao navegador do usuário e aí este consegue acessar o site.<br />
<br />
'''3)''' Do contrário o DNS Recursivo pergunta ao '''Root Server''' quem é o '''TLD (Top Level Domain)''' responsável para atender a requisição. <br />
<br />
'''4)''' O '''Root Server''' informa ao DNS Recursivo o endereço do '''TLD responsável'''. No Brasil o '''TLD''' responsável pelo '''.br''' seria o '''Registro.br'''.<br />
<br />
'''5)''' O DNS Recursivo pergunta ao '''TLD''' sobre '''wiki.brasilpeeringforum.org''' e este responde com os endereços IP dos '''DNS Autoritativos''' responsáveis pelo domínio '''brasilpeeringforum.org.'''<br />
<br />
'''6)''' O DNS Recursivo pergunta aos '''DNS Autoritativos''' pelo '''wiki.brasilpeeringforum.org''' e este responde com o '''endereço IP'''.<br />
<br />
'''7)''' Por último o DNS Recursivo devolve a informação para o navegador do usuário.<br />
<br />
Como que se dá a comunicação entre os '''DNS(s) Recursivos, Root Servers, TLDs''' e '''Autoritativos'''? Como que o navegador do usuário, após receber o IP do site, consegue chegar no servidor que tem o conteúdo? Isso só é possível devido ao protocolo chamado '''BGP (Border Gateway Protocol)''', todos os caminhos que conhecemos como rotas de destino, são anunciadas por milhares de participantes na '''Internet''' conhecidos como '''AS (Autonomous System)''', esses participantes se interligam para disponibilizar conteúdos e acessos pelo mundo aos milhares de usuários. É uma imensa rede colaborativa formada por Empresas, Universidades, Governos e todos que queiram se interconectar. Percebam que sem o '''BGP''', que serve de caminho para chegarmos nos conteúdos e sem o '''DNS (Domain Name System)''' para traduzir o nome para o endereço IP, a '''Internet''' não funcionaria e por isso precisamos cuidar muito bem desses dois serviços.<br />
<br><br><br><br><br />
Mas não acaba por aí. O '''DNS Recursivo''' tem um papel muito importante para o Provedor de Internet e que envolve segurança, qualidade de acesso à Internet e a disponibilidade do serviço entregue ao cliente. Quando bem configurado acelera as consultas dos acessos graças ao seu cache interno, mas para que isso seja percebido pelo assinante, é necessário que esteja o mais próximo possível do seu cliente.<br />
<br><br />
== Um erro que destrói a qualidade do nosso serviço ==<br />
Um erro muito comum que muitas operadoras cometem é utilizar DNS Recursivo externo, como o '''8.8.8.8''', '''1.1.1.1''' e outros, para seus clientes. Quanto mais próximo dos seus clientes, mais qualidade de serviço estará entregando a eles. Conteúdos serão entregues mais rapidamente pois serão resolvidos e armazenados em caches locais e não consultados remotamente na Internet. Para falar mais sobre isso, te convido leitor desse documento, que assista essa palestra do '''Thiago Ayub''' no '''GTER 51/GTS 37''' (2022) '''8.888 MOTIVOS PARA NÃO USAR DNS RECURSIVO EXTERNO EM SEU AS''': https://www.youtube.com/watch?v=Rsvpu5uF2Io<br />
<br />
== Objetivo ==<br />
O objetivo desta documentação não é te ensinar tudo sobre '''DNS''', '''BGP''', '''OSPF''' e nem tão pouco sobre GNU/Linux e sim te mostrar um exemplo de servidor DNS Recursivo implementado pensando em segurança, qualidade e resiliência. Usaremos em todas as nossas documentações o [https://www.debian.org/ Debian GNU/Linux], por ser uma distribuição que considero uma obra de arte criada por uma enorme comunidade séria, com vasta experiência de anos, qualidade no empacotamento dos programas, estável e com uma equipe de segurança excelente e ativa. Caso você leitor, utilize alguma outra distribuição GNU/Linux, todo conteúdo apresentado aqui pode ser aplicado em outras distros, desde que respeitando as particularidades de cada uma.<br />
<br />
Aqui construiremos um sistema do tipo '''Anycast''', ou seja, terás o serviço rodando em diversas localidades da sua Rede utilizando o mesmo endereçamento IP e que atenderá seu cliente mais próximo. Em caso de falhas, seus clientes automaticamente e de forma transparente continuarão consultando o DNS mais próximo deles. Para que ele funcione dessa forma você precisará ter uma '''Rede OSPF''' implementada no seu Provedor Internet ou algum outro protocolo como por exemplo o '''ISIS,''' mas esse documento não irá abordar o '''ISIS'''. Também utilizaremos o '''Hyperlocal''' como recurso adicional para gerar algumas proteções de segurança e velocidade na resposta relacionada aos servidores de DNS Raiz da Internet.<br />
<br />
== Diagrama ==<br />
Para exemplificar nosso servidor de DNS Recursivo, usaremos como base das explicações um diagrama demonstrando o uso do DNS Recursivo em uma Rede fictícia. Adotaremos IPs privados e reservados para demonstrar todo o ambiente do Provedor de Internet.<br />
[[Arquivo:Diagrama dns recursivo.drawio.png|esquerda|commoldura]]<br />
Nesse diagrama podemos observar alguns detalhes técnicos como por exemplo: existem '''3 servidores de DNS Recursivo''' posicionados em locais diferentes, que poderiam estar em bairros diferentes e até em cidades diferentes. Em cada servidor teremos '''2 loopbacks''' com os IPs:<br />
<br />
'''10.10.10.10/32'''<br />
<br />
'''10.10.9.9/32'''<br />
<br />
Esses IPs serão entregues pelos concentradores '''PPPoE''' ou '''IPoE''' ('''BNG''') para seus clientes como '''DNS primário''' e '''secundário'''. Podemos usar IPs privados como DNS primário e secundário em um ambiente real? Sim podemos, desde que não sejam IPs que possam ter problemas com as redes privadas dos clientes. Ex.: rede do cliente usando '''192.168.0.0/24'''. Se entregarmos o DNS sendo '''192.168.0.10''' e '''192.168.0.20''' teremos problemas e o cliente ficará sem Internet, porque '''192.168.0.10''' e '''192.168.0.20''' fazem parte da rede '''192.168.0.0/24'''.<br />
<br />
Agora entregando '''10.10.10.10''' e '''10.10.9.9''' não teríamos problemas com a rede '''192.168.0.0/24'''.<br />
<br />
'''Motivos para usarmos IPs privados:'''<br />
* O principal motivo está relacionado com a segurança, uma vez que sendo um IP privado, não pode sofrer ataques DDoS direcionados diretamente para ele, vindos da Internet.<br />
* Nem mesmo o cliente da sua rede conhece os '''IPs públicos''' utilizados para recursividade na Internet.<br />
* Memorizar os IPs '''10.10.10.10''' e '''10.10.9.9''' é tão fácil quanto memorizar o '''8.8.8.8''' e o '''1.1.1.1'''. Mais fácil para o seu técnico guardar essa informação e utilizar onde for necessário.<br />
Cada servidor DNS Recursivo possui um '''IPv4 público''', aqui representado por '''198.18.x.x/27''' e um '''IPv6 global''' representado por um IP dentro do prefixo '''2001:db8::/32'''. Cada servidor precisa ter os seus próprios IPs e são através destes IPs que as consultas de DNS serão realizadas na Internet.<br />
<br />
Nessa topologia usando '''Anycast''', o cliente será sempre atendido pelo '''DNS Recursivo''' mais próximo, desde que os pesos no '''OSPF''' estejam ajustados corretamente.<br />
<br><br><br><br><br><br><br />
== Dados do servidor ==<br />
Podemos utilizar um sistema virtualizado ou não. Sistemas virtualizados são bem vindos pois são mais simples quando precisamos fazer backups, levantar outros sistemas sem complicações e se precisarmos restaurar rapidamente algum sistema que ficou indisponível por algum motivo. A configuração abaixo tem capacidade para atender algo em torno a '''50.000 assinantes ou mais'''. O DNS Recursivo é um serviço que pode ser utilizado até mesmo em um '''Raspberry Pi''' e atender operações pequenas, nesse caso com o intuito de economizar energia e espaço. Nosso foco aqui é montar uma rede de '''DNS Recursivo Anycast com HyperLocal'''. Como comentei acima o servidor deve ficar o mais próximo dos clientes para termos a '''menor latência possível''' e '''sempre menor que 5ms''' entre o cliente e o servidor.<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Memória<br />
!Disco<br />
!Sistema<br />
|-<br />
|2.4Ghz 6 cores<br />
|16G DDR4<br />
|30G<br />
|Debian 11 amd64 (Bullseye)<br />
|}<br />
<br />
== Softwares utilizados ==<br />
* Debian 11 amd64 (Bullseye) instalação mínima.<br />
<br />
* [https://frrouting.org/ FRRouting].<br />
* Unbound.<br />
* IRQBalance.<br />
* Chrony (NTP/NTS).<br />
* Shell script em bash.<br />
<br />
== Funcionalidades que teremos ==<br />
* Sistema em Anycast.<br />
* Hyperlocal.<br />
* Controle de acesso por <abbr>ACL</abbr>.<br />
* RPZ (Response Policy Zone).<br />
* Bloqueio de consultas do tipo ANY.<br />
* QNAME minimization habilitado. (habilitado por default no Unbound)<br />
* Recursividade em IPv4 e IPv6.<br />
* DNSSEC habilitado.<br />
* <abbr>DoH (DNS</abbr> over HTTPS) habilitado.<br />
<br />
== Monitoramento ==<br />
O monitoramento é algo bem específico e não é o foco deste documento mas é extremamente importante que você monitore seus servidores de DNS por alguma ferramenta como o Zabbix. Aqui mostrarei apenas como enviar as informações para o Zabbix. Algumas coisas que você deveria monitorar nos servidores de DNS Recursivo:<br />
* Serviço do unbound parou.<br />
* Perda de pacotes.<br />
* Latência alta de pacotes.<br />
* Lentidão na resolução de queries.<br />
* CPU alta.<br />
* Load alto.<br />
* Memória com uso alto.<br />
* Disco com pouco espaço.<br />
* Queda brusca nas queries.<br />
* A recursividade parou de funcionar.<br />
* A recursividade voltou a funcionar.<br />
Este abaixo é um exemplo de monitoramento de um sistema de DNS Recursivo que atende 50.000 assinantes:<br />
[[Arquivo:Grafana dns.png|nenhum|commoldura]]<br />
<br />
== Configurando a Rede ==<br />
Nossa documentação será baseada no diagrama apresentado acima e por isso configuraremos apenas um dos três servidores, porque os outros serão configurados da mesma forma, só que com dados diferentes. Para tanto assumirei que já temos um sistema Debian instalado com o mínimo de pacotes e somente com sshd, para que possamos acessar remotamente mais tarde. '''Não instale um ambiente gráfico no servidor''', você não deve querer fazer isso por diversos motivos e os principais: primeiro porque não é um Desktop e segundo porque o ambiente gráfico devoraria toda a memória com recursos que não seriam úteis aqui.<br />
<br />
Em '''/etc/network/interfaces''' deixaremos assim:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto lo:0<br />
iface lo:0 inet static<br />
address 10.10.10.10/32<br />
<br />
auto lo:1<br />
iface lo:1 inet static<br />
address 10.10.9.9/32<br />
<br />
# The primary network interface<br />
auto ens18<br />
iface ens18 inet static<br />
address 198.18.1.10/27<br />
gateway 198.18.1.1<br />
<br />
iface ens18 inet6 static<br />
address 2001:db8::faca:198:18:1:10/64<br />
gateway 2001:db8::faca:198:18:1:1<br />
<br />
# The secondary network interface<br />
auto ens18:0<br />
iface ens18:0 inet static<br />
address 172.16.0.6/30<br />
Nesse cenário temos as duas '''loopbacks''' com os IPs '''10.10.10.10''' e '''10.10.9.9''' que serão anunciados via OSPF para a rede e serem entregues aos clientes via BNG. Os IPs '''198.18.1.10''' e '''2001:db8::faca:198:18:1:10''' serão usados para fazerem a recursividade na Internet tanto em IPv4 quanto em IPv6. Esses IPs não devem ser divulgados para clientes; os IPs públicos são dedicados apenas para essa finalidade.<br />
<br />
== Configuração dos repositórios Debian ==<br />
Deixe o arquivo '''/etc/apt/sources.list''' conforme abaixo:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
Após a configuração vamos instalar alguns pacotes necessários e outros úteis:<br />
# apt update && apt full-upgrade<br />
# apt install net-tools nftables htop iotop sipcalc tcpdump curl gnupg rsync wget host dnsutils mtr-tiny bmon sudo tmux whois ethtool dnstop<br />
<br />
== Fazendo algum tuning no sistema ==<br />
Em '''/etc/sysctl.conf''' adicionamos no final do arquivo essas instruções:<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
Estamos fazendo algumas melhorias de memória, algumas relacionadas a '''conntrack''' porque se for usar um filtro de pacotes stateful, como o '''Netfilter/IPTables''' ou '''Netfilter/NFTables''', o valor default da tabela é pequeno e dependendo da situação, se estourar essa tabela, as consultas de DNS terão problemas também. O DNS Recursivo não deve ficar aberto para qualquer um na Internet, ele deve ser liberado apenas para seus clientes. Podemos fazer através das ACLs do Unbound e pelo filtro de pacotes. O último parâmetro diz respeito ao uso de swap, por padrão o Debian permite o uso de swap após 40% do uso da memória, nesse caso estamos dizendo para o sistema usar o swap com 90% de uso da memória.<br />
<br />
Precisamos adicionar o módulo '''nf_conntrack''' em '''/etc/modules''' para que seja carregado em tempo de boot, senão os parâmetros de '''conntrack''' que colocamos em '''/etc/sysctl.conf''' não serão carregados.<br />
# echo nf_conntrack >> /etc/modules<br />
# modprobe nf_conntrack<br />
# sysctl -p<br />
<br />
== Instalando o FRRouting ==<br />
O FRRouting é o programa que usaremos para fazer os anúncios das nossas loopbacks via OSPF. Nesse documento usaremos a versão 8.x e para isso precisaremos configurar o repositório oficial do FRRouting e instalar os pacotes:<br />
# echo "deb <nowiki>https://deb.frrouting.org/frr</nowiki> bullseye frr-8" > /etc/apt/sources.list.d/frr.list<br />
# curl -s <nowiki>https://deb.frrouting.org/frr/keys.asc</nowiki> | apt-key add -<br />
# apt update<br />
# apt install frr frr-doc frr-pythontools<br />
Aconselho depois de instalar os pacotes, marcá-los para não atualizar juntamente com os demais pacotes, isso é para evitar de ocorrer alguma atualização no FRRouting, que torne o serviço instável por algum motivo. Não que isso vá ocorrer, mas é melhor fazer essa atualização quando realmente for necessário.<br />
# apt-mark hold frr frr-doc frr-pythontools<br />
Após esse comando acima, o sistema manterá a instalação original do pacote intacta. Para desbloquear basta executar o comando abaixo:<br />
# apt-mark unhold frr frr-doc frr-pythontools<br />
<br />
== Removendo o APPARMOR ==<br />
O '''APPARMOR''' às vezes causa mais problemas que solução e se não for fazer uma completa configuração nele, é melhor desabilitá-lo. Para fazer isso efetivamente, o procedimento é esse abaixo:<br />
# mkdir -p /etc/default/grub.d<br />
# echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=0"' | tee /etc/default/grub.d/apparmor.cfg<br />
# update-grub<br />
# reboot<br />
<br />
== Instalando o Unbound ==<br />
Nesse momento ainda não iremos configurar o Unbound, apenas instalar o pacote e acertar o ambiente. Vamos instalar o unbound do backports porque este já possui suporte ao DoH que veremos mais à frente.<br />
# apt -t bullseye-backports install unbound<br />
# mkdir -p /var/log/unbound<br />
# touch /var/log/unbound/unbound.log<br />
# chown -R unbound:unbound /var/log/unbound/<br />
# cd /etc/unbound<br />
# wget -c <nowiki>ftp://FTP.INTERNIC.NET/domain/named.cache</nowiki><br />
# systemctl restart unbound<br />
Configurando o logrotate:<br />
cat << EOF > /etc/logrotate.d/unbound<br />
/var/log/unbound/unbound.log {<br />
rotate 5<br />
weekly<br />
postrotate<br />
unbound-control log_reopen<br />
endscript<br />
}<br />
EOF<br />
Reiniciando o serviço:<br />
# systemctl restart logrotate.service<br />
<br />
== Preparando o monitoramento do seu DNS Recursivo ==<br />
O monitoramento do seu DNS Recursivo é muito importante e para isso vamos usar um '''template para Zabbix''', que modifiquei juntamente com o seu shell script e que enviará os dados para o seu Zabbix server via '''zabbix-sender'''. O projeto original está aqui '''https://github.com/jeftedelima/Unbound-DNS<nowiki/>.''' O xml alterado está aqui '''https://github.com/gondimcodes/template_zabbix_dns_unbound'''. Embora seja antigo é perfeitamente importável no Zabbix 6.0, por exemplo.<br />
<br />
'''<nowiki/>'''<br />
<br />
Teremos um shell script que você precisará colocar no seu '''/etc/crontab'''. No exemplo abaixo assumi que o shell script está em '''/root/scripts'''. De 5 em 5 minutos os dados serão enviados para o seu Zabbix server.<br />
*/5 * * * * root /root/scripts/unboundSend.sh '''IP_zabbix_server''' '''nome_do_host''' 1> /dev/null<br />
Na linha acima, troque o '''IP_zabbix_server''' pelo '''IP do seu servidor Zabbix''' e o '''nome_do_host''' pelo '''hostname''' '''do seu DNS Recursivo'''. Você precisará instalar o pacote '''zabbix-sender''' no seu DNS Recursivo pois ele será usado para enviar os dados para o Zabbix server.<br />
<br />
Abaixo o '''unboundSend.sh''' também alterado com inclusão de mais dados:<br />
#!/bin/bash<br />
# @Jefte de Lima Ferreira<br />
# jeftedelima at gmail dot com<br />
# CRON Example<br />
# Contributor: Marcelo Gondim - gondim at gmail dot com<br />
# */5 **** root sh /home/dir/unboundSend.sh 192.168.10.1 Unbound 1> /dev/null<br />
<br />
if [ -z ${1} ] || [ -z ${2} ] ; then<br />
echo "You need to specify the IP address of zabbix server and hostname of your DNS Unbound on zabbix"<br />
echo "Usage example: ./unboundSend.sh 192.168.10.1 UnboundServer"<br />
exit 1<br />
fi<br />
<br />
# ZABBIX_SERVER IP<br />
IP_ZABBIX=$1<br />
# NAME Unbound on Zabbix<br />
NAME_HOST=$2<br />
DIR_TEMP=/var/tmp/<br />
FILE="${DIR_TEMP}dump_unbound_control_stats.txt"<br />
unbound-control stats > ${FILE}<br />
<br />
TOTAL_NUM_QUERIES=$(cat ${FILE} | grep -w 'total.num.queries' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEHITS=$(cat ${FILE} | grep -w 'total.num.cachehits' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEMISS=$(cat ${FILE} | grep -w 'total.num.cachemiss' | cut -d '=' -f2)<br />
TOTAL_NUM_PREFETCH=$(cat ${FILE} | grep -w 'total.num.prefetch' | cut -d '=' -f2)<br />
TOTAL_NUM_RECURSIVEREPLIES=$(cat ${FILE} | grep -w 'total.num.recursivereplies' | cut -d '=' -f2)<br />
<br />
TOTAL_REQ_MAX=$(cat ${FILE} | grep -w 'total.requestlist.max' | cut -d '=' -f2)<br />
TOTAL_REQ_AVG=$(cat ${FILE} | grep -w 'total.requestlist.avg' | cut -d '=' -f2)<br />
TOTAL_REQ_OVERWRITTEN=$(cat ${FILE} | grep -w 'total.requestlist.overwritten' | cut -d '=' -f2)<br />
TOTAL_REQ_EXCEEDED=$(cat ${FILE} | grep -w 'total.requestlist.exceeded' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_ALL=$(cat ${FILE} | grep -w 'total.requestlist.current.all' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_USER=$(cat ${FILE} | grep -w 'total.requestlist.current.user' | cut -d '=' -f2)<br />
<br />
TOTAL_TCPUSAGE=$(cat ${FILE} | grep -w 'total.tcpusage' | cut -d '=' -f2)<br />
<br />
NUM_QUERY_TYPE_A=$(cat ${FILE} | grep -w 'num.query.type.A' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NS=$(cat ${FILE} | grep -w 'num.query.type.NS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_MX=$(cat ${FILE} | grep -w 'num.query.type.MX' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TXT=$(cat ${FILE} | grep -w 'num.query.type.TXT' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_PTR=$(cat ${FILE} | grep -w 'num.query.type.PTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_AAAA=$(cat ${FILE} | grep -w 'num.query.type.AAAA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SRV=$(cat ${FILE} | grep -w 'num.query.type.SRV' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SOA=$(cat ${FILE} | grep -w 'num.query.type.SOA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HTTPS=$(cat ${FILE} | grep -w 'num.query.type.HTTPS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TYPE0=$(cat ${FILE} | grep -w 'num.query.type.TYPE0' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_CNAME=$(cat ${FILE} | grep -w 'num.query.type.CNAME' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_WKS=$(cat ${FILE} | grep -w 'num.query.type.WKS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HINFO=$(cat ${FILE} | grep -w 'num.query.type.HINFO' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_X25=$(cat ${FILE} | grep -w 'num.query.type.X25' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NAPTR=$(cat ${FILE} | grep -w 'num.query.type.NAPTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DS=$(cat ${FILE} | grep -w 'num.query.type.DS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DNSKEY=$(cat ${FILE} | grep -w 'num.query.type.DNSKEY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TLSA=$(cat ${FILE} | grep -w 'num.query.type.TLSA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SVCB=$(cat ${FILE} | grep -w 'num.query.type.SVCB' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SPF=$(cat ${FILE} | grep -w 'num.query.type.SPF' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_ANY=$(cat ${FILE} | grep -w 'num.query.type.ANY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_OTHER=$(cat ${FILE} | grep -w 'num.query.type.other' | cut -d '=' -f2)<br />
<br />
NUM_ANSWER_RCODE_NOERROR=$(cat ${FILE} | grep -w 'num.answer.rcode.NOERROR' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_NXDOMAIN=$(cat ${FILE} | grep -w 'num.answer.rcode.NXDOMAIN' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_SERVFAIL=$(cat ${FILE} | grep -w 'num.answer.rcode.SERVFAIL' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_REFUSED=$(cat ${FILE} | grep -w 'num.answer.rcode.REFUSED' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_nodata=$(cat ${FILE} | grep -w 'num.answer.rcode.nodata' | cut -d '=' -f2)<br />
NUM_ANSWER_secure=$(cat ${FILE} | grep -w 'num.answer.secure' | cut -d '=' -f2)<br />
<br />
# Sending info to zabbix_server, if variables is not empty!<br />
[ -z ${TOTAL_NUM_QUERIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.queries -o ${TOTAL_NUM_QUERIES}<br />
[ -z ${TOTAL_NUM_CACHEHITS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachehits -o ${TOTAL_NUM_CACHEHITS}<br />
[ -z ${TOTAL_NUM_CACHEMISS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachemiss -o ${TOTAL_NUM_CACHEMISS}<br />
[ -z ${TOTAL_NUM_PREFETCH} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.prefetch -o ${TOTAL_NUM_PREFETCH}<br />
[ -z ${TOTAL_NUM_RECURSIVEREPLIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.recursivereplies -o ${TOTAL_NUM_RECURSIVEREPLIES}<br />
<br />
[ -z ${TOTAL_REQ_MAX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.max -o ${TOTAL_REQ_MAX}<br />
[ -z ${TOTAL_REQ_AVG} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.avg -o ${TOTAL_REQ_AVG}<br />
[ -z ${TOTAL_REQ_OVERWRITTEN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.overwritten -o ${TOTAL_REQ_OVERWRITTEN}<br />
[ -z ${TOTAL_REQ_EXCEEDED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.exceeded -o ${TOTAL_REQ_EXCEEDED}<br />
[ -z ${TOTAL_REQ_CURRENT_ALL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.all -o ${TOTAL_REQ_CURRENT_ALL}<br />
[ -z ${TOTAL_REQ_CURRENT_USER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.user -o ${TOTAL_REQ_CURRENT_USER}<br />
<br />
[ -z ${TOTAL_TCPUSAGE} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.tcpusage -o ${TOTAL_TCPUSAGE}<br />
<br />
[ -z ${NUM_QUERY_TYPE_A} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.a -o ${NUM_QUERY_TYPE_A}<br />
[ -z ${NUM_QUERY_TYPE_NS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ns -o ${NUM_QUERY_TYPE_NS}<br />
[ -z ${NUM_QUERY_TYPE_MX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.mx -o ${NUM_QUERY_TYPE_MX}<br />
[ -z ${NUM_QUERY_TYPE_TXT} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.txt -o ${NUM_QUERY_TYPE_TXT}<br />
[ -z ${NUM_QUERY_TYPE_PTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ptr -o ${NUM_QUERY_TYPE_PTR}<br />
[ -z ${NUM_QUERY_TYPE_AAAA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.aaaa -o ${NUM_QUERY_TYPE_AAAA}<br />
[ -z ${NUM_QUERY_TYPE_SRV} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.srv -o ${NUM_QUERY_TYPE_SRV}<br />
[ -z ${NUM_QUERY_TYPE_SOA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.soa -o ${NUM_QUERY_TYPE_SOA}<br />
[ -z ${NUM_QUERY_TYPE_HTTPS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.https -o ${NUM_QUERY_TYPE_HTTPS}<br />
[ -z ${NUM_QUERY_TYPE_TYPE0} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.type0 -o ${NUM_QUERY_TYPE_TYPE0}<br />
[ -z ${NUM_QUERY_TYPE_CNAME} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.cname -o ${NUM_QUERY_TYPE_CNAME}<br />
[ -z ${NUM_QUERY_TYPE_WKS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.wks -o ${NUM_QUERY_TYPE_WKS}<br />
[ -z ${NUM_QUERY_TYPE_HINFO} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.hinfo -o ${NUM_QUERY_TYPE_HINFO}<br />
[ -z ${NUM_QUERY_TYPE_X25} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.X25 -o ${NUM_QUERY_TYPE_X25}<br />
[ -z ${NUM_QUERY_TYPE_NAPTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.naptr -o ${NUM_QUERY_TYPE_NAPTR}<br />
[ -z ${NUM_QUERY_TYPE_DS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ds -o ${NUM_QUERY_TYPE_DS}<br />
[ -z ${NUM_QUERY_TYPE_DNSKEY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.dnskey -o ${NUM_QUERY_TYPE_DNSKEY}<br />
[ -z ${NUM_QUERY_TYPE_TLSA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.tlsa -o ${NUM_QUERY_TYPE_TLSA}<br />
[ -z ${NUM_QUERY_TYPE_SVCB} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.svcb -o ${NUM_QUERY_TYPE_SVCB}<br />
[ -z ${NUM_QUERY_TYPE_SPF} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.spf -o ${NUM_QUERY_TYPE_SPF}<br />
[ -z ${NUM_QUERY_TYPE_ANY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.any -o ${NUM_QUERY_TYPE_ANY}<br />
[ -z ${NUM_QUERY_TYPE_OTHER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.other -o ${NUM_QUERY_TYPE_OTHER}<br />
<br />
[ -z ${NUM_ANSWER_RCODE_NOERROR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NOERROR -o ${NUM_ANSWER_RCODE_NOERROR}<br />
[ -z ${NUM_ANSWER_RCODE_NXDOMAIN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NXDOMAIN -o ${NUM_ANSWER_RCODE_NXDOMAIN}<br />
[ -z ${NUM_ANSWER_RCODE_SERVFAIL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.SERVFAIL -o ${NUM_ANSWER_RCODE_SERVFAIL}<br />
[ -z ${NUM_ANSWER_RCODE_REFUSED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.REFUSED -o ${NUM_ANSWER_RCODE_REFUSED}<br />
[ -z ${NUM_ANSWER_RCODE_nodata} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.nodata -o ${NUM_ANSWER_RCODE_nodata}<br />
[ -z ${NUM_ANSWER_secure} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.secure -o ${NUM_ANSWER_secure}<br />
No Zabbix será registrado dados como esses abaixo e posteriormente pode ser montado um Grafana com eles:<br />
[[Arquivo:Zabbix dns01.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns02.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns03.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns04.png|nenhum|commoldura]]<br />
<br />
== Balanceando o processamento e mantendo a hora certa ==<br />
Vamos instalar 2 programas agora, o IRQBalance e o Chrony. O primeiro para balancear a carga entre os cores e o segundo para manter a data e hora certas no sistema:<br />
# apt install irqbalance<br />
# systemctl enable irqbalance<br />
# apt install chrony<br />
Após a instalação do Chrony edite o arquivo /etc/chrony/chrony.conf, comente e a linha abaixo e adicione seus servidores NTP. Caso não tenha servidores NTP, estou colocando os do NIC.br aqui.<br />
#pool 2.debian.pool.ntp.org iburst<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
<br />
# systemctl restart chronyd.service<br />
Cheque com o '''chronyc''' se os servidores estão OK:<br />
# chronyc sourcestats<br />
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev<br />
==============================================================================<br />
a.st1.ntp.br 10 5 155m -0.027 0.030 -71us 51us<br />
b.st1.ntp.br 11 7 344m +0.068 0.079 +23ms 382us<br />
c.st1.ntp.br 6 3 344m +0.026 0.037 -124us 92us<br />
200.20.186.76 9 3 138m -0.022 0.031 +172us 42us<br />
<br />
# chronyc sources<br />
MS Name/IP address Stratum Poll Reach LastRx Last sample<br />
===============================================================================<br />
^* a.st1.ntp.br 1 10 377 588 +487us[ +397us] +/- 12ms<br />
^- b.st1.ntp.br 2 10 377 830 +23ms[ +23ms] +/- 49ms<br />
^+ c.st1.ntp.br 2 10 21 1038 -147us[ -242us] +/- 17ms<br />
^+ 200.20.186.76 1 10 377 1032 +381us[ +285us] +/- 15ms<br />
<br />
== Configurando o FRRouting ==<br />
Nesse ponto iremos configurar o '''FRRouting''' para enviar os IPs das '''loopbacks''' e o '''/30''' para o nosso PE do diagrama. Em '''/etc/frr/daemons''' habilite o parâmetro conforme abaixo:<br />
ospfd=yes<br />
Edite o arquivo '''/etc/frr/frr.conf''' e deixe com o conteúdo abaixo, para ficar conforme nosso diagrama do projeto. Apenas troque '''<SENHA>''' por uma senha para fechar o OSPF com mais segurança. Essa senha deve ser usada dos dois lados.<br />
frr version 8.2.2<br />
frr defaults traditional<br />
hostname dns-recursivo-01<br />
log syslog informational<br />
no ip forwarding<br />
no ipv6 forwarding<br />
service integrated-vtysh-config<br />
!<br />
interface ens18<br />
ip ospf message-digest-key 5 md5 '''<SENHA>'''<br />
ip ospf network point-to-point<br />
exit<br />
!<br />
router ospf<br />
ospf router-id 172.16.0.6<br />
network 10.10.10.10/32 area 0.0.0.0<br />
network 10.10.9.9/32 area 0.0.0.0<br />
network 172.16.0.4/30 area 0.0.0.0<br />
area 0 authentication message-digest<br />
exit<br />
!<br />
<br />
# systemctl restart frr.service<br />
Cheque se está tudo OK com o OSPF e verifique no PE se está recebendo os prefixos anunciados.<br />
# vtysh -c 'show ip ospf neighbor'<br />
<br />
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL<br />
172.16.0.5 1 Full/- 10m49s 35.310s 172.16.0.5 ens18:172.16.0.6 0 0 0<br />
<br />
# vtysh -c 'show ip ospf neighbor detail'<br />
<br />
Neighbor 172.16.0.5, interface address 172.16.0.5<br />
In the area 0.0.0.0 via interface ens18<br />
Neighbor priority is 1, State is Full/-, 5 state changes<br />
Most recent state change statistics:<br />
Progressive change 21w3d15h ago<br />
DR is 0.0.0.0, BDR is 0.0.0.0<br />
Options 18 *|-|-|EA|-|-|E|-<br />
Dead timer due in 34.685s<br />
Database Summary List 0<br />
Link State Request List 0<br />
Link State Retransmission List 0<br />
Thread Inactivity Timer on<br />
Thread Database Description Retransmision off<br />
Thread Link State Request Retransmission on<br />
Thread Link State Update Retransmission on<br />
<br />
Graceful restart Helper info:<br />
Graceful Restart HELPER Status : None<br />
<br />
== Configurando o Unbound ==<br />
Abaixo a configuração que usaremos nos servidores atentando para o detalhe do '''num-threads''', esse deve ter o valor igual ao número de CPUs do servidor.<br />
<br />
Também os IPs utilizados em '''outgoing-interface''' que serão diferentes em cada servidor, esses serão os IPs usados para '''recursividade'''. Consulte o manual do Unbound para obter mais informações sobre cada parâmetro listado na configuração.<br />
<br />
O tuning no Unbound pode ser alterado conforme abaixo:<br />
num-threads = nº CPUs<br />
so-reuseport = yes<br />
*-slabs = potência de 2 próximo ao num-threads<br />
msg-cache-size = 1G (quantidade de memória pra usar de cache)<br />
rrset-cache-size = 2 * msg-cache-size<br />
outgoing-range = 8192<br />
num-queries-per-thread = 4096<br />
so-rcvbuf e so-sndbuf = 4m ou 8m para servidores com muita requisição<br />
Agora vamos criar nosso arquivo de configuração base em '''/etc/unbound/unbound.conf.d/local.conf''':<br />
server:<br />
verbosity: 1<br />
statistics-interval: 0<br />
statistics-cumulative: no<br />
extended-statistics: yes<br />
num-threads: 6<br />
serve-expired: yes<br />
interface: 127.0.0.1<br />
interface: 10.10.10.10<br />
interface: 10.10.9.9<br />
interface: 172.16.0.6<br />
interface: ::1<br />
interface-automatic: no<br />
outgoing-interface: 198.18.1.10<br />
outgoing-interface: 2001:db8::faca:198:18:1:10<br />
outgoing-range: 8192<br />
outgoing-num-tcp: 1024<br />
incoming-num-tcp: 2048<br />
so-rcvbuf: 4m<br />
so-sndbuf: 4m<br />
so-reuseport: yes<br />
edns-buffer-size: 1232<br />
msg-cache-size: 1G<br />
msg-cache-slabs: 4<br />
num-queries-per-thread: 4096<br />
rrset-cache-size: 2G<br />
rrset-cache-slabs: 4<br />
infra-cache-slabs: 4<br />
do-ip4: yes<br />
do-ip6: yes<br />
do-udp: yes<br />
do-tcp: yes<br />
chroot: ""<br />
username: "unbound"<br />
directory: "/etc/unbound"<br />
logfile: "/var/log/unbound/unbound.log"<br />
use-syslog: no<br />
log-time-ascii: yes<br />
log-queries: no<br />
pidfile: "/var/run/unbound.pid"<br />
root-hints: "/etc/unbound/named.cache"<br />
hide-identity: yes<br />
hide-version: yes<br />
unwanted-reply-threshold: 10000000<br />
prefetch: yes<br />
prefetch-key: yes<br />
rrset-roundrobin: yes<br />
minimal-responses: yes<br />
module-config: "respip validator iterator"<br />
val-clean-additional: yes<br />
val-log-level: 1<br />
key-cache-slabs: 4<br />
deny-any: yes<br />
access-control: 198.18.0.0/22 allow<br />
access-control: 2001:db8::/32 allow<br />
<br />
rpz:<br />
name: rpz.block.host.local.zone<br />
zonefile: /etc/unbound/rpz.block.hosts.zone<br />
rpz-action-override: nxdomain<br />
<br />
python:<br />
<br />
auth-zone:<br />
name: "."<br />
master: "b.root-servers.net"<br />
master: "c.root-servers.net"<br />
master: "d.root-servers.net"<br />
master: "f.root-servers.net"<br />
master: "g.root-servers.net"<br />
master: "k.root-servers.net"<br />
master: "lax.xfr.dns.icann.org"<br />
master: "iad.xfr.dns.icann.org"<br />
fallback-enabled: yes<br />
for-downstream: no<br />
for-upstream: yes<br />
zonefile: "/var/lib/unbound/root.zone"<br />
No parâmetro '''interface''' colocamos os IPs que serão usados para consulta dos clientes como o '''10.10.10.10''' e o '''10.10.9.9'''. Ali repare que coloquei também o IP privado '''172.16.0.6''', isso porque cada servidor terá o seu IP privado e este deve ser usado pelo seu sistema de monitoramento para checar cada servidor. No '''outgoing-interface''' teremos os IPs, tanto '''IPv4''' quanto '''IPv6''', para que seja feita a recursividade na Internet utilizando eles. Não tem '''IPv6''' ainda na sua rede? Dê uma olhada nesse artigo. Outro parâmetro importante é o '''access-control''' e é através dele que liberamos os prefixos IP para consultarem no nosso DNS Recursivo. No exemplo estou liberando todo o prefixo '''198.18.0.0/22''' e o prefixo '''2001:db8::/32'''. Além da ACL no Unbound, recomendo que crie um filtro de pacotes com iptables ou nftables protegendo seu sistema e liberando as portas '''53/UDP''', '''53/TCP''' e '''443/TCP''' apenas para seus clientes. Falarei sobre a '''443/TCP''' mais para frente nessa mesma documentação.<br />
<br />
Agora criaremos o arquivo '''RPZ''' ('''Response Policy Zones'''). Esse arquivo contém os sites que serão bloqueados via '''<abbr>DNS</abbr> Recursivo'''. São aqueles sites que às vezes você recebe um Ofício da Justiça solicitando o bloqueio deles. Não entrarei no mérito da efetividade desses bloqueios, porque muitos de vocês sabem que tecnicamente, existem formas de se fazer um bypass através desses bloqueios. Contudo vamos deixar nosso ambiente preparado para esses bloqueios e por isso crie o arquivo '''/etc/unbound/rpz.block.hosts.zone''' com esse conteúdo de exemplo:<br />
$TTL 2h<br />
@ IN SOA localhost. root.localhost. (2 6h 1h 1w 2h)<br />
IN NS localhost.<br />
; RPZ manual block hosts<br />
*.josedascoves.com CNAME .<br />
josedascoves.com CNAME .<br />
No exemplo acima estamos bloqueando qualquer consulta de DNS para '''josedascoves.com''' ou qualquer coisa '''.josedascoves.com'''.<br />
<br />
Para testar podemos fazer assim do próprio servidor:<br />
# host josedascoves.com ::1<br />
Using domain server:<br />
Name: ::1<br />
Address: ::1#53<br />
Aliases:<br />
<br />
Host josedascoves.com not found: 3(NXDOMAIN)<br />
Se a resposta for '''NXDOMAIN''' então está funcionando o bloqueio. Para incluir novos bloqueios basta adicionar os domínios, um abaixo do outro, conforme o exemplo que coloquei no arquivo RPZ.<br />
<br />
== Acertando o resolv.conf ==<br />
Vamos modificar nosso /etc/resolv.conf para utilizar DNS externo. Sim você deve estar se perguntando em qual situação isso seria utilizado. Primeiro entenda que o Unbound não irá utilizar o DNS externo para fazer as consultas na Internet e sim, qualquer teste que você faça do servidor precisará apontar para o Unbound usando os IPs '''127.0.0.1''' ou '''::1'''. Faremos isso pela seguinte situação: imagine que o daemon unbound morreu mas você ainda continua com conectividade na Internet. Você conseguiria acessar qualquer local na Internet através do IP mas não através do hostname porque não conseguiria resolver nomes, seu unbound estaria fora do ar. Imagine ainda que você gostaria que seu servidor te avisasse do problema via Telegram ou e-mail. Por isso estamos utilizando um DNS externo no '''/etc/resolv.conf''', apenas para essas situações. Se você não quiser utilizar desse recurso, pode usar o '''127.0.0.1''' e '''::1''' no lugar.<br />
nameserver 8.8.8.8<br />
nameserver 8.8.4.4<br />
nameserver 2001:4860:4860::8888<br />
<br />
== Script de teste de recursividade ==<br />
Estamos montando uma '''Rede de DNS Recursivo Anycast''', então é muito importante que você monitore essa rede para saber se algum node morreu e iniciar o troubleshooting, resolver o problema e levantar o sistema novamente. Tudo isso é importante mas o cliente não deve ficar esperando até você resolver o problema, seu sistema precisa ser inteligente o suficiente para se remover da Rede quando tiver um problema e se inserir novamente, quando o problema estiver sido solucionado. Se você montar uma Rede de DNS e um dos nodes apresentar algum problema, todos os clientes atendidos por aquele node migrarão automaticamente e transparentemente para outro '''DNS Recursivo Anycast''' mais próximo. Isso se chama '''disponibilidade'''.<br />
<br />
O script '''/root/scripts/checa_dns.sh''' abaixo tem a função de fazer os testes de recursividade e checar se o daemon do unbound continua rodando. Se algo acontecer, ele para o anúncio do '''10.10.10.10''' e '''10.10.9.9''' e retorna eles quando tudo estiver resolvido.<br />
# mkdir /root/scripts<br />
<br />
#!/usr/bin/env bash<br />
#Script para teste de recursividade v2.2<br />
# Por Marcelo Gondim<br />
# Em 24-12-2022<br />
#<br />
# checa_dns.sh is free software; you can redistribute it and/or modify<br />
# it under the terms of the GNU General Public License as published by<br />
# the Free Software Foundation; either version 2 of the License, or<br />
# (at your option) any later version.<br />
#<br />
# This program is distributed in the hope that it will be useful,<br />
# but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
# GNU General Public License for more details.<br />
#<br />
# You should have received a copy of the GNU General Public License<br />
# along with this program; if not, write to the Free Software<br />
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
#-----------------------------------------------------------------------<br />
#Informe um domínio por linha:<br />
dominios_testar=(<br />
www.google.com<br />
www.terra.com.br<br />
www.uol.com.br<br />
www.globo.com<br />
www.facebook.com<br />
www.youtube.com<br />
www.twitch.com<br />
www.discord.com<br />
www.debian.org<br />
www.redhat.com<br />
)<br />
corte_taxa_falha=100 #Porcentagem de falha para executar uma ação<br />
#-----------------------------------------------------------------------<br />
remove_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" != "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME morreu!" | /usr/local/sbin/telegram-notify --error --text -<br />
fi<br />
}<br />
<br />
adiciona_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" == "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME retornou do inferno!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
}<br />
<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
#echo "Servidor $HOSTNAME morreu DNS mas tentando levantar!" | /usr/local/sbin/telegram-notify --error --text -<br />
systemctl restart unbound<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
#echo "Servidor $HOSTNAME servico DNS voltou mas tinha morrido!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
<br />
qt_falhas=0<br />
qt_total="${#dominios_testar[@]}"<br />
echo "total_dominios: $qt_total"<br />
for site in "${dominios_testar[@]}"<br />
do<br />
resolver="127.0.0.1"<br />
echo -e " - dominio $site - $resolver - \c"<br />
host $site $resolver &> /dev/null<br />
if [ $? -ne 0 ]; then<br />
((qt_falhas++))<br />
echo -e "[Falhou]"<br />
else<br />
echo -e "[OK]"<br />
fi<br />
done<br />
<br />
taxa_falha=$((qt_falhas*100/qt_total))<br />
echo "Falhas $qt_falhas/$qt_total ($taxa_falha%)"<br />
<br />
if [ "$taxa_falha" -ge "$corte_taxa_falha" ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
adiciona_ospf<br />
Se rodarmos o script manualmente veremos isto:<br />
# /root/scripts/checa_dns.sh<br />
total_dominios: 10<br />
- dominio www.google.com - 127.0.0.1 - [OK]<br />
- dominio www.terra.com.br - 127.0.0.1 - [OK]<br />
- dominio www.uol.com.br - 127.0.0.1 - [OK]<br />
- dominio www.globo.com - 127.0.0.1 - [OK]<br />
- dominio www.facebook.com - 127.0.0.1 - [OK]<br />
- dominio www.youtube.com - 127.0.0.1 - [OK]<br />
- dominio www.twitch.com - 127.0.0.1 - [OK]<br />
- dominio www.discord.com - 127.0.0.1 - [OK]<br />
- dominio www.debian.org - 127.0.0.1 - [OK]<br />
- dominio www.redhat.com - 127.0.0.1 - [OK]<br />
Falhas 0/10 (0%)<br />
Se acontecer 100% de falhas o script irá remover os anúncios do OSPF. Se o daemon do unbound morrer, ele tentará reiniciá-lo. Se tudo normalizar o script irá retornar os anúncios para o OSPF. Deixei comentado no script as partes que enviariam uma notificação para o Telegram. Existem diversas documentações sobre isso na Internet, eu mesmo tenho uma. Assim que eu publicar aqui, atualizo essa documentação e sinta-se à vontade de modificar como desejar.<br />
# chmod 700 /root/scripts/checa_dns.sh<br />
Adicione a linha abaixo em seu '''/etc/crontab''':<br />
*/1 * * * * root /root/scripts/checa_dns.sh<br />
<br />
== Habilitando o DoH (<abbr>DNS</abbr> over HTTPS) - opcional ==<br />
Para habilitar o '''DoH''' no Unbound é bem simples mas só é possível com a versão '''1.17.1''' que vem no '''bullseye-backports''' e que virá na próxima versão do '''Debian 12 (Bookworm)'''. O recurso do '''DoH''' vem para trazer mais segurança e privacidade para o usuário. É um recurso muito pouco utilizado ainda mas que seu cliente pode vir a pedir algum dia.<br />
<br />
Você precisará gerar certificados SSL legítimos e para isso você poderá usar o '''Let's Encrypt''' só que de uma forma não tão convencional.<br />
<br />
Na sequência vamos instalar o Let's Encrypt para gerarmos nosso certificado SSL:<br />
# apt install letsencrypt<br />
Escolha um '''hostname''' para ser usado no nosso '''DoH''' e aponte ele no seu DNS Autoritativo para seus IPs 10.10.10.10 e 10.10.9.9. Aqui vamos usar o seguinte como exemplo: '''doh.brasilpeeringforum.org'''. Para gerarmos nosso certificado iremos usar o tipo '''DNS-01''', ele não necessita que tenhamos um servidor web rodando no servidor e nem tão pouco levanta um serviço na porta 80 para checar o hostname. Ele utiliza o DNS como validador e vai te solicitar que crie um registro '''CNAME''' no seu '''DNS Autoritativo''' para provar que você tem o controle sobre aquele hostname. Antes disso vamos instalar um programa em Python para podermos automatizar nossa renovação de certificado no futuro. Esse programa se encontra '''[https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py aqui]''' mas vou deixá-lo abaixo já modificado o interpretador.<br />
<br />
Crie o arquivo '''/etc/letsencrypt/acme-dns-auth.py''' com o conteúdo abaixo:<br />
#!/usr/bin/env python3<br />
import json<br />
import os<br />
import requests<br />
import sys<br />
<br />
### EDIT THESE: Configuration values ###<br />
<br />
# URL to acme-dns instance<br />
ACMEDNS_URL = "<nowiki>https://auth.acme-dns.io</nowiki>"<br />
# Path for acme-dns credential storage<br />
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"<br />
# Whitelist for address ranges to allow the updates from<br />
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]<br />
ALLOW_FROM = []<br />
# Force re-registration. Overwrites the already existing acme-dns accounts.<br />
FORCE_REGISTER = False<br />
<br />
### DO NOT EDIT BELOW THIS POINT ###<br />
### HERE BE DRAGONS ###<br />
<br />
DOMAIN = os.environ["CERTBOT_DOMAIN"]<br />
if DOMAIN.startswith("*."):<br />
DOMAIN = DOMAIN[2:]<br />
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN<br />
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]<br />
<br />
<br />
class AcmeDnsClient(object):<br />
"""<br />
Handles the communication with ACME-DNS API<br />
"""<br />
<br />
def __init__(self, acmedns_url):<br />
self.acmedns_url = acmedns_url<br />
<br />
def register_account(self, allowfrom):<br />
"""Registers a new ACME-DNS account"""<br />
<br />
if allowfrom:<br />
# Include whitelisted networks to the registration call<br />
reg_data = {"allowfrom": allowfrom}<br />
res = requests.post(self.acmedns_url+"/register",<br />
data=json.dumps(reg_data))<br />
else:<br />
res = requests.post(self.acmedns_url+"/register")<br />
if res.status_code == 201:<br />
# The request was successful<br />
return res.json()<br />
else:<br />
# Encountered an error<br />
msg = ("Encountered an error while trying to register a new acme-dns "<br />
"account. HTTP status {}, Response body: {}")<br />
print(msg.format(res.status_code, res.text))<br />
sys.exit(1)<br />
<br />
def update_txt_record(self, account, txt):<br />
"""Updates the TXT challenge record to ACME-DNS subdomain."""<br />
update = {"subdomain": account['subdomain'], "txt": txt}<br />
headers = {"X-Api-User": account['username'],<br />
"X-Api-Key": account['password'],<br />
"Content-Type": "application/json"}<br />
res = requests.post(self.acmedns_url+"/update",<br />
headers=headers,<br />
data=json.dumps(update))<br />
if res.status_code == 200:<br />
# Successful update<br />
return<br />
else:<br />
msg = ("Encountered an error while trying to update TXT record in "<br />
"acme-dns. \n"<br />
"------- Request headers:\n{}\n"<br />
"------- Request body:\n{}\n"<br />
"------- Response HTTP status: {}\n"<br />
"------- Response body: {}")<br />
s_headers = json.dumps(headers, indent=2, sort_keys=True)<br />
s_update = json.dumps(update, indent=2, sort_keys=True)<br />
s_body = json.dumps(res.json(), indent=2, sort_keys=True)<br />
print(msg.format(s_headers, s_update, res.status_code, s_body))<br />
sys.exit(1)<br />
<br />
class Storage(object):<br />
def __init__(self, storagepath):<br />
self.storagepath = storagepath<br />
self._data = self.load()<br />
<br />
def load(self):<br />
"""Reads the storage content from the disk to a dict structure"""<br />
data = dict()<br />
filedata = ""<br />
try:<br />
with open(self.storagepath, 'r') as fh:<br />
filedata = fh.read()<br />
except IOError as e:<br />
if os.path.isfile(self.storagepath):<br />
# Only error out if file exists, but cannot be read<br />
print("ERROR: Storage file exists but cannot be read")<br />
sys.exit(1)<br />
try:<br />
data = json.loads(filedata)<br />
except ValueError:<br />
if len(filedata) > 0:<br />
# Storage file is corrupted<br />
print("ERROR: Storage JSON is corrupted")<br />
sys.exit(1)<br />
return data<br />
<br />
def save(self):<br />
"""Saves the storage content to disk"""<br />
serialized = json.dumps(self._data)<br />
try:<br />
with os.fdopen(os.open(self.storagepath,<br />
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:<br />
fh.truncate()<br />
fh.write(serialized)<br />
except IOError as e:<br />
print("ERROR: Could not write storage file.")<br />
sys.exit(1)<br />
<br />
def put(self, key, value):<br />
"""Puts the configuration value to storage and sanitize it"""<br />
# If wildcard domain, remove the wildcard part as this will use the<br />
# same validation record name as the base domain<br />
if key.startswith("*."):<br />
key = key[2:]<br />
self._data[key] = value<br />
<br />
def fetch(self, key):<br />
"""Gets configuration value from storage"""<br />
try:<br />
return self._data[key]<br />
except KeyError:<br />
return None<br />
<br />
if __name__ == "__main__":<br />
# Init<br />
client = AcmeDnsClient(ACMEDNS_URL)<br />
storage = Storage(STORAGE_PATH)<br />
<br />
# Check if an account already exists in storage<br />
account = storage.fetch(DOMAIN)<br />
if FORCE_REGISTER or not account:<br />
# Create and save the new account<br />
account = client.register_account(ALLOW_FROM)<br />
storage.put(DOMAIN, account)<br />
storage.save()<br />
<br />
# Display the notification for the user to update the main zone<br />
msg = "Please add the following CNAME record to your main DNS zone:\n{}"<br />
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])<br />
print(msg.format(cname))<br />
<br />
# Update the TXT record in acme-dns instance<br />
client.update_txt_record(account, VALIDATION_TOKEN)<br />
<br />
# chmod +x /etc/letsencrypt/acme-dns-auth.py<br />
Usaremos a seguinte instrução para criar nosso certificado:<br />
# certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d doh.brasilpeeringforum.org<br />
Saving debug log to /var/log/letsencrypt/letsencrypt.log<br />
Plugins selected: Authenticator manual, Installer None<br />
Cert is due for renewal, auto-renewing...<br />
Renewing an existing certificate for doh.brasilpeeringforum.org<br />
Performing the following challenges:<br />
dns-01 challenge for doh.brasilpeeringforum.org<br />
Running manual-auth-hook command: /etc/letsencrypt/acme-dns-auth.py<br />
Output from manual-auth-hook command acme-dns-auth.py:<br />
Please add the following CNAME record to your main DNS zone:<br />
_acme-challenge.doh.brasilpeeringforum.org CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
<br />
Waiting for verification...<br />
<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Challenges loaded. Press continue to submit to CA. Pass "-v" for more info about<br />
challenges.<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Press Enter to Continue<br />
Nesse momento você cria o registro '''CNAME''' no seu DNS Autoritativo conforme ele solicitou: '''_acme-challenge.doh.brasilpeeringforum.org IN CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.''' e somente depois de criado e checado no DNS, você pressiona o '''Enter''' para continuar. Você pode checar dessa forma:<br />
# host -t cname _acme-challenge.doh.brasilpeeringforum.org<br />
_acme-challenge.doh.brasilpeeringforum.org is an alias for b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
Para que nosso certificado seja automaticamente renovado colocaremos no '''/etc/crontab''' a seguinte linha abaixo:<br />
00 00 1 * * root /usr/bin/certbot -q renew<br />
Acima temos a instrução para renovação automática do certificado. Repare que você vai precisar também copiar esse certificado para seus outros servidores, escolha um servidor para manter o certificado sempre atualizado e crie um script que faça a mesma cópia remotamente para os outros servidores. O '''scp''' e o '''rsync''' são seus aliados nisso.<br />
<br />
=== Configurando o Unbound ===<br />
Em nosso '''/etc/unbound/unbound.conf.d/local.conf''', adicionaremos no bloco "'''server:'''" o seguinte:<br />
interface: 10.10.10.10@443<br />
interface: 10.10.9.9@443<br />
tls-service-key: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/privkey.pem"<br />
tls-service-pem: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/fullchain.pem"<br />
Para usar o recurso do '''DoH''' você precisará habilitar o recurso no seu navegador e informar a URL. Vou colocar o exemplo do '''Google Chrome''': Digite '''chrome://settings/security?search=dns''' no seu Chrome e ative '''Usar DNS seguro''', selecione '''Personalizado''' e adicione nossa URL:<br />
[[Arquivo:Doh bpf2.png|nenhum|commoldura]]<br />
<br />
== Finalizando ==<br />
Aqui finalizamos nosso projeto para uma Rede de DNS(s) Recursivos Anycast com Hyperlocal. Esse projeto é escalável, seguro, resiliente e você entregará muito mais qualidade de Internet para o seu cliente. Pare de entregar o '''8.8.8.8''' para os seus clientes, você está contribuindo para uma Internet mais lenta, sem a qualidade que o seu cliente merece. Investi meu tempo, que é muito pouco, para deixar esse documento para a comunidade, para você melhorar o seu ISP, para dar um UP! nele, então vamos começar 2023 com o pé direito. O que acha?<br />
<br />
Como prova de conceito, uma imagem abaixo onde temos uma Rede em produção de DNS(s) Recursivos Anycast e apontando exatamente o momento em que houve alguma situação que fez com que as queries de DNS, convergissem de um node para outro, de forma transparente e automática para o cliente. Podemos notar também que ao ser resolvido o problema, o tráfego retornou para o seu node correto:<br />
[[Arquivo:Convergencia.png|nenhum|commoldura]]<br />
<br />
== KINDNS (Stands for Knowledge-Sharing and Instantiating Norms for DNS and Naming Security) ==<br />
Achou que havia terminado? Agora que você tem a capacidade de montar uma '''Rede de DNS Recursivo''' com todas essas features acima, com todas as ferramentas que foram comentadas, o que acha de certificar o que fez?<br />
<br />
Assim como o [https://www.manrs.org/ MANRS] veio para certificar nosso sistema de roteamento na Internet, agora temos o [https://kindns.org/ KINDNS] para certificar que nossos sistemas de DNS estão bem feitos e dentro dos padrões de segurança. Existem '''7 ações''' que podem ser certificadas para nossos DNS Recursivos e estão aqui em https://kindns.org/shared-private-resolvers/. Com essa nossa documentação, se bem aplicada, você pode se candidatar ao KINDNS e ter seu ASN listado aqui https://kindns.org/participants/<br />
<br />
Obter e manter o '''MANRS''' e '''KINDNS''' demonstra seu compromisso com as Boas Práticas, contribui para termos uma '''Internet''' mais segura e te abre portas para novos negócios que possam exigir essas conformidades.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]<br />
__FORCARTDC__</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=DNS_Recursivo_Anycast_Hyperlocal&diff=3660DNS Recursivo Anycast Hyperlocal2023-12-05T00:35:41Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
==Introdução==<br />
Você sabe como funciona a Internet? Essa é uma pergunta que meu amigo '''Thiago Ayub''' sempre faz aos seus candidatos à vagas de emprego e não importa o quanto tenham de experiência em '''Engenharia de Redes''', todos sempre travam nesse momento. Todos estão sempre prontos e preparados para resolver os problemas mais cabeludos em '''BGP''', '''OSPF''', '''MPLS''', etc mas travam com essa simples pergunta. Para contextualizar e visualizarmos melhor vamos nos atentar à imagem abaixo e uma explicação simplificada de como funciona:<br />
[[Arquivo:Dns hierarquia.png|esquerda|commoldura]]<br />
Tudo começa com um usuário sentado confortavelmente e querendo acessar um conteúdo disponível na Internet. Ele digita em seu navegador preferido a URL: '''<nowiki>https://wiki.brasilpeeringforum.org</nowiki>''',<br />
<br />
'''<big>1)</big>''' <big>O</big> <big>navegador irá requisitar do '''DNS Recursivo''' utilizado pelo usuário, o '''endereço IP''' que responde pelo nome '''wiki.'''</big>'''brasilpeeringforum.org'''<big>. Isso porque todos os acessos se dão na Internet através do '''endereço''' '''IP''' e não através do '''nome'''. Imaginem se tivéssemos que decorar os endereços IPs de todos os sites e serviços que quiséssemos acessar na Internet?</big> <br />
<br />
<big>'''2)''' Nosso DNS Recursivo checa se a informação consta em seu cache.</big> Se a informação existir ela é devolvida ao navegador do usuário e aí este consegue acessar o site.<br />
<br />
'''3)''' Do contrário o DNS Recursivo pergunta ao '''Root Server''' quem é o '''TLD (Top Level Domain)''' responsável para atender a requisição. <br />
<br />
'''4)''' O '''Root Server''' informa ao DNS Recursivo o endereço do '''TLD responsável'''. No Brasil o '''TLD''' responsável pelo '''.br''' seria o '''Registro.br'''.<br />
<br />
'''5)''' O DNS Recursivo pergunta ao '''TLD''' sobre '''wiki.brasilpeeringforum.org''' e este responde com os endereços IP dos '''DNS Autoritativos''' responsáveis pelo domínio '''brasilpeeringforum.org.'''<br />
<br />
'''6)''' O DNS Recursivo pergunta aos '''DNS Autoritativos''' pelo '''wiki.brasilpeeringforum.org''' e este responde com o '''endereço IP'''.<br />
<br />
'''7)''' Por último o DNS Recursivo devolve a informação para o navegador do usuário.<br />
<br />
Como que se dá a comunicação entre os '''DNS(s) Recursivos, Root Servers, TLDs''' e '''Autoritativos'''? Como que o navegador do usuário, após receber o IP do site, consegue chegar no servidor que tem o conteúdo? Isso só é possível devido ao protocolo chamado '''BGP (Border Gateway Protocol)''', todos os caminhos que conhecemos como rotas de destino, são anunciadas por milhares de participantes na '''Internet''' conhecidos como '''AS (Autonomous System)''', esses participantes se interligam para disponibilizar conteúdos e acessos pelo mundo aos milhares de usuários. É uma imensa rede colaborativa formada por Empresas, Universidades, Governos e todos que queiram se interconectar. Percebam que sem o '''BGP''', que serve de caminho para chegarmos nos conteúdos e sem o '''DNS (Domain Name System)''' para traduzir o nome para o endereço IP, a '''Internet''' não funcionaria e por isso precisamos cuidar muito bem desses dois serviços.<br />
<br><br><br><br><br />
Mas não acaba por aí. O '''DNS Recursivo''' tem um papel muito importante para o Provedor de Internet e que envolve segurança, qualidade de acesso à Internet e a disponibilidade do serviço entregue ao cliente. Quando bem configurado acelera as consultas dos acessos graças ao seu cache interno, mas para que isso seja percebido pelo assinante, é necessário que esteja o mais próximo possível do seu cliente.<br />
<br><br />
== Um erro que destrói a qualidade do nosso serviço ==<br />
Um erro muito comum que muitas operadoras cometem é utilizar DNS Recursivo externo, como o '''8.8.8.8''', '''1.1.1.1''' e outros, para seus clientes. Quanto mais próximo dos seus clientes, mais qualidade de serviço estará entregando a eles. Conteúdos serão entregues mais rapidamente pois serão resolvidos e armazenados em caches locais e não consultados remotamente na Internet. Para falar mais sobre isso, te convido leitor desse documento, que assista essa palestra do '''Thiago Ayub''' no '''GTER 51/GTS 37''' (2022) '''8.888 MOTIVOS PARA NÃO USAR DNS RECURSIVO EXTERNO EM SEU AS''': https://www.youtube.com/watch?v=Rsvpu5uF2Io<br />
<br />
== Objetivo ==<br />
O objetivo desta documentação não é te ensinar tudo sobre '''DNS''', '''BGP''', '''OSPF''' e nem tão pouco sobre GNU/Linux e sim te mostrar um exemplo de servidor DNS Recursivo implementado pensando em segurança, qualidade e resiliência. Usaremos em todas as nossas documentações o [https://www.debian.org/ Debian GNU/Linux], por ser uma distribuição que considero uma obra de arte criada por uma enorme comunidade séria, com vasta experiência de anos, qualidade no empacotamento dos programas, estável e com uma equipe de segurança excelente e ativa. Caso você leitor, utilize alguma outra distribuição GNU/Linux, todo conteúdo apresentado aqui pode ser aplicado em outras distros, desde que respeitando as particularidades de cada uma.<br />
<br />
Aqui construiremos um sistema do tipo '''Anycast''', ou seja, terás o serviço rodando em diversas localidades da sua Rede utilizando o mesmo endereçamento IP e que atenderá seu cliente mais próximo. Em caso de falhas, seus clientes automaticamente e de forma transparente continuarão consultando o DNS mais próximo deles. Para que ele funcione dessa forma você precisará ter uma '''Rede OSPF''' implementada no seu Provedor Internet ou algum outro protocolo como por exemplo o '''ISIS,''' mas esse documento não irá abordar o '''ISIS'''. Também utilizaremos o '''Hyperlocal''' como recurso adicional para gerar algumas proteções de segurança e velocidade na resposta relacionada aos servidores de DNS Raiz da Internet.<br />
<br />
== Diagrama ==<br />
Para exemplificar nosso servidor de DNS Recursivo, usaremos como base das explicações um diagrama demonstrando o uso do DNS Recursivo em uma Rede fictícia. Adotaremos IPs privados e reservados para demonstrar todo o ambiente do Provedor de Internet.<br />
[[Arquivo:Diagrama dns recursivo.drawio.png|esquerda|commoldura]]<br />
Nesse diagrama podemos observar alguns detalhes técnicos como por exemplo: existem '''3 servidores de DNS Recursivo''' posicionados em locais diferentes, que poderiam estar em bairros diferentes e até em cidades diferentes. Em cada servidor teremos '''2 loopbacks''' com os IPs:<br />
<br />
'''10.10.10.10/32'''<br />
<br />
'''10.10.9.9/32'''<br />
<br />
Esses IPs serão entregues pelos concentradores '''PPPoE''' ou '''IPoE''' ('''BNG''') para seus clientes como '''DNS primário''' e '''secundário'''. Podemos usar IPs privados como DNS primário e secundário em um ambiente real? Sim podemos, desde que não sejam IPs que possam ter problemas com as redes privadas dos clientes. Ex.: rede do cliente usando '''192.168.0.0/24'''. Se entregarmos o DNS sendo '''192.168.0.10''' e '''192.168.0.20''' teremos problemas e o cliente ficará sem Internet, porque '''192.168.0.10''' e '''192.168.0.20''' fazem parte da rede '''192.168.0.0/24'''.<br />
<br />
Agora entregando '''10.10.10.10''' e '''10.10.9.9''' não teríamos problemas com a rede '''192.168.0.0/24'''.<br />
<br />
'''Motivos para usarmos IPs privados:'''<br />
* O principal motivo está relacionado com a segurança, uma vez que sendo um IP privado, não pode sofrer ataques DDoS direcionados diretamente para ele, vindos da Internet.<br />
* Nem mesmo o cliente da sua rede conhece os '''IPs públicos''' utilizados para recursividade na Internet.<br />
* Memorizar os IPs '''10.10.10.10''' e '''10.10.9.9''' é tão fácil quanto memorizar o '''8.8.8.8''' e o '''1.1.1.1'''. Mais fácil para o seu técnico guardar essa informação e utilizar onde for necessário.<br />
Cada servidor DNS Recursivo possui um '''IPv4 público''', aqui representado por '''198.18.x.x/27''' e um '''IPv6 global''' representado por um IP dentro do prefixo '''2001:db8::/32'''. Cada servidor precisa ter os seus próprios IPs e são através destes IPs que as consultas de DNS serão realizadas na Internet.<br />
<br />
Nessa topologia usando '''Anycast''', o cliente será sempre atendido pelo '''DNS Recursivo''' mais próximo, desde que os pesos no '''OSPF''' estejam ajustados corretamente.<br />
<br><br><br><br><br><br><br />
== Dados do servidor ==<br />
Podemos utilizar um sistema virtualizado ou não. Sistemas virtualizados são bem vindos pois são mais simples quando precisamos fazer backups, levantar outros sistemas sem complicações e se precisarmos restaurar rapidamente algum sistema que ficou indisponível por algum motivo. A configuração abaixo tem capacidade para atender algo em torno a '''50.000 assinantes ou mais'''. O DNS Recursivo é um serviço que pode ser utilizado até mesmo em um '''Raspberry Pi''' e atender operações pequenas, nesse caso com o intuito de economizar energia e espaço. Nosso foco aqui é montar uma rede de '''DNS Recursivo Anycast com HyperLocal'''. Como comentei acima o servidor deve ficar o mais próximo dos clientes para termos a '''menor latência possível''' e '''sempre menor que 5ms''' entre o cliente e o servidor.<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Memória<br />
!Disco<br />
!Sistema<br />
|-<br />
|2.4Ghz 6 cores<br />
|16G DDR4<br />
|30G<br />
|Debian 11 amd64 (Bullseye)<br />
|}<br />
<br />
== Softwares utilizados ==<br />
* Debian 11 amd64 (Bullseye) instalação mínima.<br />
<br />
* [https://frrouting.org/ FRRouting].<br />
* Unbound.<br />
* IRQBalance.<br />
* Chrony (NTP/NTS).<br />
* Shell script em bash.<br />
<br />
== Funcionalidades que teremos ==<br />
* Sistema em Anycast.<br />
* Hyperlocal.<br />
* Controle de acesso por <abbr>ACL</abbr>.<br />
* RPZ (Response Policy Zone).<br />
* Bloqueio de consultas do tipo ANY.<br />
* QNAME minimization habilitado. (habilitado por default no Unbound)<br />
* Recursividade em IPv4 e IPv6.<br />
* DNSSEC habilitado.<br />
* <abbr>DoH (DNS</abbr> over HTTPS) habilitado.<br />
<br />
== Monitoramento ==<br />
O monitoramento é algo bem específico e não é o foco deste documento mas é extremamente importante que você monitore seus servidores de DNS por alguma ferramenta como o Zabbix. Aqui mostrarei apenas como enviar as informações para o Zabbix. Algumas coisas que você deveria monitorar nos servidores de DNS Recursivo:<br />
* Serviço do unbound parou.<br />
* Perda de pacotes.<br />
* Latência alta de pacotes.<br />
* Lentidão na resolução de queries.<br />
* CPU alta.<br />
* Load alto.<br />
* Memória com uso alto.<br />
* Disco com pouco espaço.<br />
* Queda brusca nas queries.<br />
* A recursividade parou de funcionar.<br />
* A recursividade voltou a funcionar.<br />
Este abaixo é um exemplo de monitoramento de um sistema de DNS Recursivo que atende 50.000 assinantes:<br />
[[Arquivo:Grafana dns.png|nenhum|commoldura]]<br />
<br />
== Configurando a Rede ==<br />
Nossa documentação será baseada no diagrama apresentado acima e por isso configuraremos apenas um dos três servidores, porque os outros serão configurados da mesma forma, só que com dados diferentes. Para tanto assumirei que já temos um sistema Debian instalado com o mínimo de pacotes e somente com sshd, para que possamos acessar remotamente mais tarde. '''Não instale um ambiente gráfico no servidor''', você não deve querer fazer isso por diversos motivos e os principais: primeiro porque não é um Desktop e segundo porque o ambiente gráfico devoraria toda a memória com recursos que não seriam úteis aqui.<br />
<br />
Em '''/etc/network/interfaces''' deixaremos assim:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto lo:0<br />
iface lo:0 inet static<br />
address 10.10.10.10/32<br />
<br />
auto lo:1<br />
iface lo:1 inet static<br />
address 10.10.9.9/32<br />
<br />
# The primary network interface<br />
auto ens18<br />
iface ens18 inet static<br />
address 198.18.1.10/27<br />
gateway 198.18.1.1<br />
<br />
iface ens18 inet6 static<br />
address 2001:db8::faca:198:18:1:10/64<br />
gateway 2001:db8::faca:198:18:1:1<br />
<br />
# The secondary network interface<br />
auto ens18:0<br />
iface ens18:0 inet static<br />
address 172.16.0.6/30<br />
Nesse cenário temos as duas '''loopbacks''' com os IPs '''10.10.10.10''' e '''10.10.9.9''' que serão anunciados via OSPF para a rede e serem entregues aos clientes via BNG. Os IPs '''198.18.1.10''' e '''2001:db8::faca:198:18:1:10''' serão usados para fazerem a recursividade na Internet tanto em IPv4 quanto em IPv6. Esses IPs não devem ser divulgados para clientes; os IPs públicos são dedicados apenas para essa finalidade.<br />
<br />
== Configuração dos repositórios Debian ==<br />
Deixe o arquivo '''/etc/apt/sources.list''' conforme abaixo:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
Após a configuração vamos instalar alguns pacotes necessários e outros úteis:<br />
# apt update && apt full-upgrade<br />
# apt install net-tools nftables htop iotop sipcalc tcpdump curl gnupg rsync wget host dnsutils mtr-tiny bmon sudo tmux whois ethtool dnstop<br />
<br />
== Fazendo algum tuning no sistema ==<br />
Em '''/etc/sysctl.conf''' adicionamos no final do arquivo essas instruções:<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
Estamos fazendo algumas melhorias de memória, algumas relacionadas a '''conntrack''' porque se for usar um filtro de pacotes stateful, como o '''Netfilter/IPTables''' ou '''Netfilter/NFTables''', o valor default da tabela é pequeno e dependendo da situação, se estourar essa tabela, as consultas de DNS terão problemas também. O DNS Recursivo não deve ficar aberto para qualquer um na Internet, ele deve ser liberado apenas para seus clientes. Podemos fazer através das ACLs do Unbound e pelo filtro de pacotes. O último parâmetro diz respeito ao uso de swap, por padrão o Debian permite o uso de swap após 40% do uso da memória, nesse caso estamos dizendo para o sistema usar o swap com 90% de uso da memória.<br />
<br />
Precisamos adicionar o módulo '''nf_conntrack''' em '''/etc/modules''' para que seja carregado em tempo de boot, senão os parâmetros de '''conntrack''' que colocamos em '''/etc/sysctl.conf''' não serão carregados.<br />
# echo nf_conntrack >> /etc/modules<br />
# modprobe nf_conntrack<br />
# sysctl -p<br />
<br />
== Instalando o FRRouting ==<br />
O FRRouting é o programa que usaremos para fazer os anúncios das nossas loopbacks via OSPF. Nesse documento usaremos a versão 8.x e para isso precisaremos configurar o repositório oficial do FRRouting e instalar os pacotes:<br />
# echo "deb <nowiki>https://deb.frrouting.org/frr</nowiki> bullseye frr-8" > /etc/apt/sources.list.d/frr.list<br />
# curl -s <nowiki>https://deb.frrouting.org/frr/keys.asc</nowiki> | apt-key add -<br />
# apt update<br />
# apt install frr frr-doc frr-pythontools<br />
Aconselho depois de instalar os pacotes, marcá-los para não atualizar juntamente com os demais pacotes, isso é para evitar de ocorrer alguma atualização no FRRouting, que torne o serviço instável por algum motivo. Não que isso vá ocorrer, mas é melhor fazer essa atualização quando realmente for necessário.<br />
# apt-mark hold frr frr-doc frr-pythontools<br />
Após esse comando acima, o sistema manterá a instalação original do pacote intacta. Para desbloquear basta executar o comando abaixo:<br />
# apt-mark unhold frr frr-doc frr-pythontools<br />
<br />
== Removendo o APPARMOR ==<br />
O '''APPARMOR''' às vezes causa mais problemas que solução e se não for fazer uma completa configuração nele, é melhor desabilitá-lo. Para fazer isso efetivamente, o procedimento é esse abaixo:<br />
# mkdir -p /etc/default/grub.d<br />
# echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=0"' | tee /etc/default/grub.d/apparmor.cfg<br />
# update-grub<br />
# reboot<br />
<br />
== Instalando o Unbound ==<br />
Nesse momento ainda não iremos configurar o Unbound, apenas instalar o pacote e acertar o ambiente. Vamos instalar o unbound do backports porque este já possui suporte ao DoH que veremos mais à frente.<br />
# apt -t bullseye-backports install unbound<br />
# mkdir -p /var/log/unbound<br />
# touch /var/log/unbound/unbound.log<br />
# chown -R unbound:unbound /var/log/unbound/<br />
# cd /etc/unbound<br />
# wget -c <nowiki>ftp://FTP.INTERNIC.NET/domain/named.cache</nowiki><br />
# systemctl restart unbound<br />
Configurando o logrotate:<br />
cat << EOF > /etc/logrotate.d/unbound<br />
/var/log/unbound/unbound.log {<br />
rotate 5<br />
weekly<br />
postrotate<br />
unbound-control log_reopen<br />
endscript<br />
}<br />
EOF<br />
Reiniciando o serviço:<br />
# systemctl restart logrotate.service<br />
<br />
== Preparando o monitoramento do seu DNS Recursivo ==<br />
O monitoramento do seu DNS Recursivo é muito importante e para isso vamos usar um '''template para Zabbix''', que modifiquei juntamente com o seu shell script e que enviará os dados para o seu Zabbix server via '''zabbix-sender'''. O projeto original está aqui '''https://github.com/jeftedelima/Unbound-DNS<nowiki/>.''' Fiz umas mudanças e o xml alterado está aqui '''https://github.com/gondimcodes/template_zabbix_dns_unbound'''. Embora seja antigo é perfeitamente importável no Zabbix 6.0, por exemplo.<br />
<br />
Teremos um shell script que você precisará colocar no seu '''/etc/crontab'''. No exemplo abaixo assumi que o shell script está em '''/root/scripts'''. De 5 em 5 minutos os dados serão enviados para o seu Zabbix server.<br />
*/5 * * * * root /root/scripts/unboundSend.sh '''IP_zabbix_server''' '''nome_do_host''' 1> /dev/null<br />
Na linha acima, troque o '''IP_zabbix_server''' pelo '''IP do seu servidor Zabbix''' e o '''nome_do_host''' pelo '''hostname''' '''do seu DNS Recursivo'''. Você precisará instalar o pacote '''zabbix-sender''' no seu DNS Recursivo pois ele será usado para enviar os dados para o Zabbix server.<br />
<br />
Abaixo o '''unboundSend.sh''' também alterado com inclusão de mais dados:<br />
#!/bin/bash<br />
# @Jefte de Lima Ferreira<br />
# jeftedelima at gmail dot com<br />
# CRON Example<br />
# Contributor: Marcelo Gondim - gondim at gmail dot com<br />
# */5 **** root sh /home/dir/unboundSend.sh 192.168.10.1 Unbound 1> /dev/null<br />
<br />
if [ -z ${1} ] || [ -z ${2} ] ; then<br />
echo "You need to specify the IP address of zabbix server and hostname of your DNS Unbound on zabbix"<br />
echo "Usage example: ./unboundSend.sh 192.168.10.1 UnboundServer"<br />
exit 1<br />
fi<br />
<br />
# ZABBIX_SERVER IP<br />
IP_ZABBIX=$1<br />
# NAME Unbound on Zabbix<br />
NAME_HOST=$2<br />
DIR_TEMP=/var/tmp/<br />
FILE="${DIR_TEMP}dump_unbound_control_stats.txt"<br />
unbound-control stats > ${FILE}<br />
<br />
TOTAL_NUM_QUERIES=$(cat ${FILE} | grep -w 'total.num.queries' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEHITS=$(cat ${FILE} | grep -w 'total.num.cachehits' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEMISS=$(cat ${FILE} | grep -w 'total.num.cachemiss' | cut -d '=' -f2)<br />
TOTAL_NUM_PREFETCH=$(cat ${FILE} | grep -w 'total.num.prefetch' | cut -d '=' -f2)<br />
TOTAL_NUM_RECURSIVEREPLIES=$(cat ${FILE} | grep -w 'total.num.recursivereplies' | cut -d '=' -f2)<br />
<br />
TOTAL_REQ_MAX=$(cat ${FILE} | grep -w 'total.requestlist.max' | cut -d '=' -f2)<br />
TOTAL_REQ_AVG=$(cat ${FILE} | grep -w 'total.requestlist.avg' | cut -d '=' -f2)<br />
TOTAL_REQ_OVERWRITTEN=$(cat ${FILE} | grep -w 'total.requestlist.overwritten' | cut -d '=' -f2)<br />
TOTAL_REQ_EXCEEDED=$(cat ${FILE} | grep -w 'total.requestlist.exceeded' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_ALL=$(cat ${FILE} | grep -w 'total.requestlist.current.all' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_USER=$(cat ${FILE} | grep -w 'total.requestlist.current.user' | cut -d '=' -f2)<br />
<br />
TOTAL_TCPUSAGE=$(cat ${FILE} | grep -w 'total.tcpusage' | cut -d '=' -f2)<br />
<br />
NUM_QUERY_TYPE_A=$(cat ${FILE} | grep -w 'num.query.type.A' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NS=$(cat ${FILE} | grep -w 'num.query.type.NS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_MX=$(cat ${FILE} | grep -w 'num.query.type.MX' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TXT=$(cat ${FILE} | grep -w 'num.query.type.TXT' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_PTR=$(cat ${FILE} | grep -w 'num.query.type.PTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_AAAA=$(cat ${FILE} | grep -w 'num.query.type.AAAA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SRV=$(cat ${FILE} | grep -w 'num.query.type.SRV' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SOA=$(cat ${FILE} | grep -w 'num.query.type.SOA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HTTPS=$(cat ${FILE} | grep -w 'num.query.type.HTTPS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TYPE0=$(cat ${FILE} | grep -w 'num.query.type.TYPE0' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_CNAME=$(cat ${FILE} | grep -w 'num.query.type.CNAME' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_WKS=$(cat ${FILE} | grep -w 'num.query.type.WKS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HINFO=$(cat ${FILE} | grep -w 'num.query.type.HINFO' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_X25=$(cat ${FILE} | grep -w 'num.query.type.X25' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NAPTR=$(cat ${FILE} | grep -w 'num.query.type.NAPTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DS=$(cat ${FILE} | grep -w 'num.query.type.DS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DNSKEY=$(cat ${FILE} | grep -w 'num.query.type.DNSKEY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TLSA=$(cat ${FILE} | grep -w 'num.query.type.TLSA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SVCB=$(cat ${FILE} | grep -w 'num.query.type.SVCB' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SPF=$(cat ${FILE} | grep -w 'num.query.type.SPF' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_ANY=$(cat ${FILE} | grep -w 'num.query.type.ANY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_OTHER=$(cat ${FILE} | grep -w 'num.query.type.other' | cut -d '=' -f2)<br />
<br />
NUM_ANSWER_RCODE_NOERROR=$(cat ${FILE} | grep -w 'num.answer.rcode.NOERROR' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_NXDOMAIN=$(cat ${FILE} | grep -w 'num.answer.rcode.NXDOMAIN' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_SERVFAIL=$(cat ${FILE} | grep -w 'num.answer.rcode.SERVFAIL' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_REFUSED=$(cat ${FILE} | grep -w 'num.answer.rcode.REFUSED' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_nodata=$(cat ${FILE} | grep -w 'num.answer.rcode.nodata' | cut -d '=' -f2)<br />
NUM_ANSWER_secure=$(cat ${FILE} | grep -w 'num.answer.secure' | cut -d '=' -f2)<br />
<br />
# Sending info to zabbix_server, if variables is not empty!<br />
[ -z ${TOTAL_NUM_QUERIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.queries -o ${TOTAL_NUM_QUERIES}<br />
[ -z ${TOTAL_NUM_CACHEHITS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachehits -o ${TOTAL_NUM_CACHEHITS}<br />
[ -z ${TOTAL_NUM_CACHEMISS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachemiss -o ${TOTAL_NUM_CACHEMISS}<br />
[ -z ${TOTAL_NUM_PREFETCH} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.prefetch -o ${TOTAL_NUM_PREFETCH}<br />
[ -z ${TOTAL_NUM_RECURSIVEREPLIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.recursivereplies -o ${TOTAL_NUM_RECURSIVEREPLIES}<br />
<br />
[ -z ${TOTAL_REQ_MAX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.max -o ${TOTAL_REQ_MAX}<br />
[ -z ${TOTAL_REQ_AVG} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.avg -o ${TOTAL_REQ_AVG}<br />
[ -z ${TOTAL_REQ_OVERWRITTEN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.overwritten -o ${TOTAL_REQ_OVERWRITTEN}<br />
[ -z ${TOTAL_REQ_EXCEEDED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.exceeded -o ${TOTAL_REQ_EXCEEDED}<br />
[ -z ${TOTAL_REQ_CURRENT_ALL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.all -o ${TOTAL_REQ_CURRENT_ALL}<br />
[ -z ${TOTAL_REQ_CURRENT_USER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.user -o ${TOTAL_REQ_CURRENT_USER}<br />
<br />
[ -z ${TOTAL_TCPUSAGE} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.tcpusage -o ${TOTAL_TCPUSAGE}<br />
<br />
[ -z ${NUM_QUERY_TYPE_A} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.a -o ${NUM_QUERY_TYPE_A}<br />
[ -z ${NUM_QUERY_TYPE_NS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ns -o ${NUM_QUERY_TYPE_NS}<br />
[ -z ${NUM_QUERY_TYPE_MX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.mx -o ${NUM_QUERY_TYPE_MX}<br />
[ -z ${NUM_QUERY_TYPE_TXT} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.txt -o ${NUM_QUERY_TYPE_TXT}<br />
[ -z ${NUM_QUERY_TYPE_PTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ptr -o ${NUM_QUERY_TYPE_PTR}<br />
[ -z ${NUM_QUERY_TYPE_AAAA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.aaaa -o ${NUM_QUERY_TYPE_AAAA}<br />
[ -z ${NUM_QUERY_TYPE_SRV} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.srv -o ${NUM_QUERY_TYPE_SRV}<br />
[ -z ${NUM_QUERY_TYPE_SOA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.soa -o ${NUM_QUERY_TYPE_SOA}<br />
[ -z ${NUM_QUERY_TYPE_HTTPS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.https -o ${NUM_QUERY_TYPE_HTTPS}<br />
[ -z ${NUM_QUERY_TYPE_TYPE0} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.type0 -o ${NUM_QUERY_TYPE_TYPE0}<br />
[ -z ${NUM_QUERY_TYPE_CNAME} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.cname -o ${NUM_QUERY_TYPE_CNAME}<br />
[ -z ${NUM_QUERY_TYPE_WKS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.wks -o ${NUM_QUERY_TYPE_WKS}<br />
[ -z ${NUM_QUERY_TYPE_HINFO} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.hinfo -o ${NUM_QUERY_TYPE_HINFO}<br />
[ -z ${NUM_QUERY_TYPE_X25} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.X25 -o ${NUM_QUERY_TYPE_X25}<br />
[ -z ${NUM_QUERY_TYPE_NAPTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.naptr -o ${NUM_QUERY_TYPE_NAPTR}<br />
[ -z ${NUM_QUERY_TYPE_DS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ds -o ${NUM_QUERY_TYPE_DS}<br />
[ -z ${NUM_QUERY_TYPE_DNSKEY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.dnskey -o ${NUM_QUERY_TYPE_DNSKEY}<br />
[ -z ${NUM_QUERY_TYPE_TLSA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.tlsa -o ${NUM_QUERY_TYPE_TLSA}<br />
[ -z ${NUM_QUERY_TYPE_SVCB} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.svcb -o ${NUM_QUERY_TYPE_SVCB}<br />
[ -z ${NUM_QUERY_TYPE_SPF} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.spf -o ${NUM_QUERY_TYPE_SPF}<br />
[ -z ${NUM_QUERY_TYPE_ANY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.any -o ${NUM_QUERY_TYPE_ANY}<br />
[ -z ${NUM_QUERY_TYPE_OTHER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.other -o ${NUM_QUERY_TYPE_OTHER}<br />
<br />
[ -z ${NUM_ANSWER_RCODE_NOERROR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NOERROR -o ${NUM_ANSWER_RCODE_NOERROR}<br />
[ -z ${NUM_ANSWER_RCODE_NXDOMAIN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NXDOMAIN -o ${NUM_ANSWER_RCODE_NXDOMAIN}<br />
[ -z ${NUM_ANSWER_RCODE_SERVFAIL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.SERVFAIL -o ${NUM_ANSWER_RCODE_SERVFAIL}<br />
[ -z ${NUM_ANSWER_RCODE_REFUSED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.REFUSED -o ${NUM_ANSWER_RCODE_REFUSED}<br />
[ -z ${NUM_ANSWER_RCODE_nodata} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.nodata -o ${NUM_ANSWER_RCODE_nodata}<br />
[ -z ${NUM_ANSWER_secure} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.secure -o ${NUM_ANSWER_secure}<br />
No Zabbix será registrado dados como esses abaixo e posteriormente pode ser montado um Grafana com eles:<br />
[[Arquivo:Zabbix dns01.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns02.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns03.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns04.png|nenhum|commoldura]]<br />
<br />
== Balanceando o processamento e mantendo a hora certa ==<br />
Vamos instalar 2 programas agora, o IRQBalance e o Chrony. O primeiro para balancear a carga entre os cores e o segundo para manter a data e hora certas no sistema:<br />
# apt install irqbalance<br />
# systemctl enable irqbalance<br />
# apt install chrony<br />
Após a instalação do Chrony edite o arquivo /etc/chrony/chrony.conf, comente e a linha abaixo e adicione seus servidores NTP. Caso não tenha servidores NTP, estou colocando os do NIC.br aqui.<br />
#pool 2.debian.pool.ntp.org iburst<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
<br />
# systemctl restart chronyd.service<br />
Cheque com o '''chronyc''' se os servidores estão OK:<br />
# chronyc sourcestats<br />
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev<br />
==============================================================================<br />
a.st1.ntp.br 10 5 155m -0.027 0.030 -71us 51us<br />
b.st1.ntp.br 11 7 344m +0.068 0.079 +23ms 382us<br />
c.st1.ntp.br 6 3 344m +0.026 0.037 -124us 92us<br />
200.20.186.76 9 3 138m -0.022 0.031 +172us 42us<br />
<br />
# chronyc sources<br />
MS Name/IP address Stratum Poll Reach LastRx Last sample<br />
===============================================================================<br />
^* a.st1.ntp.br 1 10 377 588 +487us[ +397us] +/- 12ms<br />
^- b.st1.ntp.br 2 10 377 830 +23ms[ +23ms] +/- 49ms<br />
^+ c.st1.ntp.br 2 10 21 1038 -147us[ -242us] +/- 17ms<br />
^+ 200.20.186.76 1 10 377 1032 +381us[ +285us] +/- 15ms<br />
<br />
== Configurando o FRRouting ==<br />
Nesse ponto iremos configurar o '''FRRouting''' para enviar os IPs das '''loopbacks''' e o '''/30''' para o nosso PE do diagrama. Em '''/etc/frr/daemons''' habilite o parâmetro conforme abaixo:<br />
ospfd=yes<br />
Edite o arquivo '''/etc/frr/frr.conf''' e deixe com o conteúdo abaixo, para ficar conforme nosso diagrama do projeto. Apenas troque '''<SENHA>''' por uma senha para fechar o OSPF com mais segurança. Essa senha deve ser usada dos dois lados.<br />
frr version 8.2.2<br />
frr defaults traditional<br />
hostname dns-recursivo-01<br />
log syslog informational<br />
no ip forwarding<br />
no ipv6 forwarding<br />
service integrated-vtysh-config<br />
!<br />
interface ens18<br />
ip ospf message-digest-key 5 md5 '''<SENHA>'''<br />
ip ospf network point-to-point<br />
exit<br />
!<br />
router ospf<br />
ospf router-id 172.16.0.6<br />
network 10.10.10.10/32 area 0.0.0.0<br />
network 10.10.9.9/32 area 0.0.0.0<br />
network 172.16.0.4/30 area 0.0.0.0<br />
area 0 authentication message-digest<br />
exit<br />
!<br />
<br />
# systemctl restart frr.service<br />
Cheque se está tudo OK com o OSPF e verifique no PE se está recebendo os prefixos anunciados.<br />
# vtysh -c 'show ip ospf neighbor'<br />
<br />
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL<br />
172.16.0.5 1 Full/- 10m49s 35.310s 172.16.0.5 ens18:172.16.0.6 0 0 0<br />
<br />
# vtysh -c 'show ip ospf neighbor detail'<br />
<br />
Neighbor 172.16.0.5, interface address 172.16.0.5<br />
In the area 0.0.0.0 via interface ens18<br />
Neighbor priority is 1, State is Full/-, 5 state changes<br />
Most recent state change statistics:<br />
Progressive change 21w3d15h ago<br />
DR is 0.0.0.0, BDR is 0.0.0.0<br />
Options 18 *|-|-|EA|-|-|E|-<br />
Dead timer due in 34.685s<br />
Database Summary List 0<br />
Link State Request List 0<br />
Link State Retransmission List 0<br />
Thread Inactivity Timer on<br />
Thread Database Description Retransmision off<br />
Thread Link State Request Retransmission on<br />
Thread Link State Update Retransmission on<br />
<br />
Graceful restart Helper info:<br />
Graceful Restart HELPER Status : None<br />
<br />
== Configurando o Unbound ==<br />
Abaixo a configuração que usaremos nos servidores atentando para o detalhe do '''num-threads''', esse deve ter o valor igual ao número de CPUs do servidor.<br />
<br />
Também os IPs utilizados em '''outgoing-interface''' que serão diferentes em cada servidor, esses serão os IPs usados para '''recursividade'''. Consulte o manual do Unbound para obter mais informações sobre cada parâmetro listado na configuração.<br />
<br />
O tuning no Unbound pode ser alterado conforme abaixo:<br />
num-threads = nº CPUs<br />
so-reuseport = yes<br />
*-slabs = potência de 2 próximo ao num-threads<br />
msg-cache-size = 1G (quantidade de memória pra usar de cache)<br />
rrset-cache-size = 2 * msg-cache-size<br />
outgoing-range = 8192<br />
num-queries-per-thread = 4096<br />
so-rcvbuf e so-sndbuf = 4m ou 8m para servidores com muita requisição<br />
Agora vamos criar nosso arquivo de configuração base em '''/etc/unbound/unbound.conf.d/local.conf''':<br />
server:<br />
verbosity: 1<br />
statistics-interval: 0<br />
statistics-cumulative: no<br />
extended-statistics: yes<br />
num-threads: 6<br />
serve-expired: yes<br />
interface: 127.0.0.1<br />
interface: 10.10.10.10<br />
interface: 10.10.9.9<br />
interface: 172.16.0.6<br />
interface: ::1<br />
interface-automatic: no<br />
outgoing-interface: 198.18.1.10<br />
outgoing-interface: 2001:db8::faca:198:18:1:10<br />
outgoing-range: 8192<br />
outgoing-num-tcp: 1024<br />
incoming-num-tcp: 2048<br />
so-rcvbuf: 4m<br />
so-sndbuf: 4m<br />
so-reuseport: yes<br />
edns-buffer-size: 1232<br />
msg-cache-size: 1G<br />
msg-cache-slabs: 4<br />
num-queries-per-thread: 4096<br />
rrset-cache-size: 2G<br />
rrset-cache-slabs: 4<br />
infra-cache-slabs: 4<br />
do-ip4: yes<br />
do-ip6: yes<br />
do-udp: yes<br />
do-tcp: yes<br />
chroot: ""<br />
username: "unbound"<br />
directory: "/etc/unbound"<br />
logfile: "/var/log/unbound/unbound.log"<br />
use-syslog: no<br />
log-time-ascii: yes<br />
log-queries: no<br />
pidfile: "/var/run/unbound.pid"<br />
root-hints: "/etc/unbound/named.cache"<br />
hide-identity: yes<br />
hide-version: yes<br />
unwanted-reply-threshold: 10000000<br />
prefetch: yes<br />
prefetch-key: yes<br />
rrset-roundrobin: yes<br />
minimal-responses: yes<br />
module-config: "respip validator iterator"<br />
val-clean-additional: yes<br />
val-log-level: 1<br />
key-cache-slabs: 4<br />
deny-any: yes<br />
access-control: 198.18.0.0/22 allow<br />
access-control: 2001:db8::/32 allow<br />
<br />
rpz:<br />
name: rpz.block.host.local.zone<br />
zonefile: /etc/unbound/rpz.block.hosts.zone<br />
rpz-action-override: nxdomain<br />
<br />
python:<br />
<br />
auth-zone:<br />
name: "."<br />
master: "b.root-servers.net"<br />
master: "c.root-servers.net"<br />
master: "d.root-servers.net"<br />
master: "f.root-servers.net"<br />
master: "g.root-servers.net"<br />
master: "k.root-servers.net"<br />
master: "lax.xfr.dns.icann.org"<br />
master: "iad.xfr.dns.icann.org"<br />
fallback-enabled: yes<br />
for-downstream: no<br />
for-upstream: yes<br />
zonefile: "/var/lib/unbound/root.zone"<br />
No parâmetro '''interface''' colocamos os IPs que serão usados para consulta dos clientes como o '''10.10.10.10''' e o '''10.10.9.9'''. Ali repare que coloquei também o IP privado '''172.16.0.6''', isso porque cada servidor terá o seu IP privado e este deve ser usado pelo seu sistema de monitoramento para checar cada servidor. No '''outgoing-interface''' teremos os IPs, tanto '''IPv4''' quanto '''IPv6''', para que seja feita a recursividade na Internet utilizando eles. Não tem '''IPv6''' ainda na sua rede? Dê uma olhada nesse artigo. Outro parâmetro importante é o '''access-control''' e é através dele que liberamos os prefixos IP para consultarem no nosso DNS Recursivo. No exemplo estou liberando todo o prefixo '''198.18.0.0/22''' e o prefixo '''2001:db8::/32'''. Além da ACL no Unbound, recomendo que crie um filtro de pacotes com iptables ou nftables protegendo seu sistema e liberando as portas '''53/UDP''', '''53/TCP''' e '''443/TCP''' apenas para seus clientes. Falarei sobre a '''443/TCP''' mais para frente nessa mesma documentação.<br />
<br />
Agora criaremos o arquivo '''RPZ''' ('''Response Policy Zones'''). Esse arquivo contém os sites que serão bloqueados via '''<abbr>DNS</abbr> Recursivo'''. São aqueles sites que às vezes você recebe um Ofício da Justiça solicitando o bloqueio deles. Não entrarei no mérito da efetividade desses bloqueios, porque muitos de vocês sabem que tecnicamente, existem formas de se fazer um bypass através desses bloqueios. Contudo vamos deixar nosso ambiente preparado para esses bloqueios e por isso crie o arquivo '''/etc/unbound/rpz.block.hosts.zone''' com esse conteúdo de exemplo:<br />
$TTL 2h<br />
@ IN SOA localhost. root.localhost. (2 6h 1h 1w 2h)<br />
IN NS localhost.<br />
; RPZ manual block hosts<br />
*.josedascoves.com CNAME .<br />
josedascoves.com CNAME .<br />
No exemplo acima estamos bloqueando qualquer consulta de DNS para '''josedascoves.com''' ou qualquer coisa '''.josedascoves.com'''.<br />
<br />
Para testar podemos fazer assim do próprio servidor:<br />
# host josedascoves.com ::1<br />
Using domain server:<br />
Name: ::1<br />
Address: ::1#53<br />
Aliases:<br />
<br />
Host josedascoves.com not found: 3(NXDOMAIN)<br />
Se a resposta for '''NXDOMAIN''' então está funcionando o bloqueio. Para incluir novos bloqueios basta adicionar os domínios, um abaixo do outro, conforme o exemplo que coloquei no arquivo RPZ.<br />
<br />
== Acertando o resolv.conf ==<br />
Vamos modificar nosso /etc/resolv.conf para utilizar DNS externo. Sim você deve estar se perguntando em qual situação isso seria utilizado. Primeiro entenda que o Unbound não irá utilizar o DNS externo para fazer as consultas na Internet e sim, qualquer teste que você faça do servidor precisará apontar para o Unbound usando os IPs '''127.0.0.1''' ou '''::1'''. Faremos isso pela seguinte situação: imagine que o daemon unbound morreu mas você ainda continua com conectividade na Internet. Você conseguiria acessar qualquer local na Internet através do IP mas não através do hostname porque não conseguiria resolver nomes, seu unbound estaria fora do ar. Imagine ainda que você gostaria que seu servidor te avisasse do problema via Telegram ou e-mail. Por isso estamos utilizando um DNS externo no '''/etc/resolv.conf''', apenas para essas situações. Se você não quiser utilizar desse recurso, pode usar o '''127.0.0.1''' e '''::1''' no lugar.<br />
nameserver 8.8.8.8<br />
nameserver 8.8.4.4<br />
nameserver 2001:4860:4860::8888<br />
<br />
== Script de teste de recursividade ==<br />
Estamos montando uma '''Rede de DNS Recursivo Anycast''', então é muito importante que você monitore essa rede para saber se algum node morreu e iniciar o troubleshooting, resolver o problema e levantar o sistema novamente. Tudo isso é importante mas o cliente não deve ficar esperando até você resolver o problema, seu sistema precisa ser inteligente o suficiente para se remover da Rede quando tiver um problema e se inserir novamente, quando o problema estiver sido solucionado. Se você montar uma Rede de DNS e um dos nodes apresentar algum problema, todos os clientes atendidos por aquele node migrarão automaticamente e transparentemente para outro '''DNS Recursivo Anycast''' mais próximo. Isso se chama '''disponibilidade'''.<br />
<br />
O script '''/root/scripts/checa_dns.sh''' abaixo tem a função de fazer os testes de recursividade e checar se o daemon do unbound continua rodando. Se algo acontecer, ele para o anúncio do '''10.10.10.10''' e '''10.10.9.9''' e retorna eles quando tudo estiver resolvido.<br />
# mkdir /root/scripts<br />
<br />
#!/usr/bin/env bash<br />
#Script para teste de recursividade v2.2<br />
# Por Marcelo Gondim<br />
# Em 24-12-2022<br />
#<br />
# checa_dns.sh is free software; you can redistribute it and/or modify<br />
# it under the terms of the GNU General Public License as published by<br />
# the Free Software Foundation; either version 2 of the License, or<br />
# (at your option) any later version.<br />
#<br />
# This program is distributed in the hope that it will be useful,<br />
# but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
# GNU General Public License for more details.<br />
#<br />
# You should have received a copy of the GNU General Public License<br />
# along with this program; if not, write to the Free Software<br />
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
#-----------------------------------------------------------------------<br />
#Informe um domínio por linha:<br />
dominios_testar=(<br />
www.google.com<br />
www.terra.com.br<br />
www.uol.com.br<br />
www.globo.com<br />
www.facebook.com<br />
www.youtube.com<br />
www.twitch.com<br />
www.discord.com<br />
www.debian.org<br />
www.redhat.com<br />
)<br />
corte_taxa_falha=100 #Porcentagem de falha para executar uma ação<br />
#-----------------------------------------------------------------------<br />
remove_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" != "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME morreu!" | /usr/local/sbin/telegram-notify --error --text -<br />
fi<br />
}<br />
<br />
adiciona_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" == "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME retornou do inferno!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
}<br />
<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
#echo "Servidor $HOSTNAME morreu DNS mas tentando levantar!" | /usr/local/sbin/telegram-notify --error --text -<br />
systemctl restart unbound<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
#echo "Servidor $HOSTNAME servico DNS voltou mas tinha morrido!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
<br />
qt_falhas=0<br />
qt_total="${#dominios_testar[@]}"<br />
echo "total_dominios: $qt_total"<br />
for site in "${dominios_testar[@]}"<br />
do<br />
resolver="127.0.0.1"<br />
echo -e " - dominio $site - $resolver - \c"<br />
host $site $resolver &> /dev/null<br />
if [ $? -ne 0 ]; then<br />
((qt_falhas++))<br />
echo -e "[Falhou]"<br />
else<br />
echo -e "[OK]"<br />
fi<br />
done<br />
<br />
taxa_falha=$((qt_falhas*100/qt_total))<br />
echo "Falhas $qt_falhas/$qt_total ($taxa_falha%)"<br />
<br />
if [ "$taxa_falha" -ge "$corte_taxa_falha" ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
adiciona_ospf<br />
Se rodarmos o script manualmente veremos isto:<br />
# /root/scripts/checa_dns.sh<br />
total_dominios: 10<br />
- dominio www.google.com - 127.0.0.1 - [OK]<br />
- dominio www.terra.com.br - 127.0.0.1 - [OK]<br />
- dominio www.uol.com.br - 127.0.0.1 - [OK]<br />
- dominio www.globo.com - 127.0.0.1 - [OK]<br />
- dominio www.facebook.com - 127.0.0.1 - [OK]<br />
- dominio www.youtube.com - 127.0.0.1 - [OK]<br />
- dominio www.twitch.com - 127.0.0.1 - [OK]<br />
- dominio www.discord.com - 127.0.0.1 - [OK]<br />
- dominio www.debian.org - 127.0.0.1 - [OK]<br />
- dominio www.redhat.com - 127.0.0.1 - [OK]<br />
Falhas 0/10 (0%)<br />
Se acontecer 100% de falhas o script irá remover os anúncios do OSPF. Se o daemon do unbound morrer, ele tentará reiniciá-lo. Se tudo normalizar o script irá retornar os anúncios para o OSPF. Deixei comentado no script as partes que enviariam uma notificação para o Telegram. Existem diversas documentações sobre isso na Internet, eu mesmo tenho uma. Assim que eu publicar aqui, atualizo essa documentação e sinta-se à vontade de modificar como desejar.<br />
# chmod 700 /root/scripts/checa_dns.sh<br />
Adicione a linha abaixo em seu '''/etc/crontab''':<br />
*/1 * * * * root /root/scripts/checa_dns.sh<br />
<br />
== Habilitando o DoH (<abbr>DNS</abbr> over HTTPS) - opcional ==<br />
Para habilitar o '''DoH''' no Unbound é bem simples mas só é possível com a versão '''1.17.1''' que vem no '''bullseye-backports''' e que virá na próxima versão do '''Debian 12 (Bookworm)'''. O recurso do '''DoH''' vem para trazer mais segurança e privacidade para o usuário. É um recurso muito pouco utilizado ainda mas que seu cliente pode vir a pedir algum dia.<br />
<br />
Você precisará gerar certificados SSL legítimos e para isso você poderá usar o '''Let's Encrypt''' só que de uma forma não tão convencional.<br />
<br />
Na sequência vamos instalar o Let's Encrypt para gerarmos nosso certificado SSL:<br />
# apt install letsencrypt<br />
Escolha um '''hostname''' para ser usado no nosso '''DoH''' e aponte ele no seu DNS Autoritativo para seus IPs 10.10.10.10 e 10.10.9.9. Aqui vamos usar o seguinte como exemplo: '''doh.brasilpeeringforum.org'''. Para gerarmos nosso certificado iremos usar o tipo '''DNS-01''', ele não necessita que tenhamos um servidor web rodando no servidor e nem tão pouco levanta um serviço na porta 80 para checar o hostname. Ele utiliza o DNS como validador e vai te solicitar que crie um registro '''CNAME''' no seu '''DNS Autoritativo''' para provar que você tem o controle sobre aquele hostname. Antes disso vamos instalar um programa em Python para podermos automatizar nossa renovação de certificado no futuro. Esse programa se encontra '''[https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py aqui]''' mas vou deixá-lo abaixo já modificado o interpretador.<br />
<br />
Crie o arquivo '''/etc/letsencrypt/acme-dns-auth.py''' com o conteúdo abaixo:<br />
#!/usr/bin/env python3<br />
import json<br />
import os<br />
import requests<br />
import sys<br />
<br />
### EDIT THESE: Configuration values ###<br />
<br />
# URL to acme-dns instance<br />
ACMEDNS_URL = "<nowiki>https://auth.acme-dns.io</nowiki>"<br />
# Path for acme-dns credential storage<br />
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"<br />
# Whitelist for address ranges to allow the updates from<br />
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]<br />
ALLOW_FROM = []<br />
# Force re-registration. Overwrites the already existing acme-dns accounts.<br />
FORCE_REGISTER = False<br />
<br />
### DO NOT EDIT BELOW THIS POINT ###<br />
### HERE BE DRAGONS ###<br />
<br />
DOMAIN = os.environ["CERTBOT_DOMAIN"]<br />
if DOMAIN.startswith("*."):<br />
DOMAIN = DOMAIN[2:]<br />
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN<br />
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]<br />
<br />
<br />
class AcmeDnsClient(object):<br />
"""<br />
Handles the communication with ACME-DNS API<br />
"""<br />
<br />
def __init__(self, acmedns_url):<br />
self.acmedns_url = acmedns_url<br />
<br />
def register_account(self, allowfrom):<br />
"""Registers a new ACME-DNS account"""<br />
<br />
if allowfrom:<br />
# Include whitelisted networks to the registration call<br />
reg_data = {"allowfrom": allowfrom}<br />
res = requests.post(self.acmedns_url+"/register",<br />
data=json.dumps(reg_data))<br />
else:<br />
res = requests.post(self.acmedns_url+"/register")<br />
if res.status_code == 201:<br />
# The request was successful<br />
return res.json()<br />
else:<br />
# Encountered an error<br />
msg = ("Encountered an error while trying to register a new acme-dns "<br />
"account. HTTP status {}, Response body: {}")<br />
print(msg.format(res.status_code, res.text))<br />
sys.exit(1)<br />
<br />
def update_txt_record(self, account, txt):<br />
"""Updates the TXT challenge record to ACME-DNS subdomain."""<br />
update = {"subdomain": account['subdomain'], "txt": txt}<br />
headers = {"X-Api-User": account['username'],<br />
"X-Api-Key": account['password'],<br />
"Content-Type": "application/json"}<br />
res = requests.post(self.acmedns_url+"/update",<br />
headers=headers,<br />
data=json.dumps(update))<br />
if res.status_code == 200:<br />
# Successful update<br />
return<br />
else:<br />
msg = ("Encountered an error while trying to update TXT record in "<br />
"acme-dns. \n"<br />
"------- Request headers:\n{}\n"<br />
"------- Request body:\n{}\n"<br />
"------- Response HTTP status: {}\n"<br />
"------- Response body: {}")<br />
s_headers = json.dumps(headers, indent=2, sort_keys=True)<br />
s_update = json.dumps(update, indent=2, sort_keys=True)<br />
s_body = json.dumps(res.json(), indent=2, sort_keys=True)<br />
print(msg.format(s_headers, s_update, res.status_code, s_body))<br />
sys.exit(1)<br />
<br />
class Storage(object):<br />
def __init__(self, storagepath):<br />
self.storagepath = storagepath<br />
self._data = self.load()<br />
<br />
def load(self):<br />
"""Reads the storage content from the disk to a dict structure"""<br />
data = dict()<br />
filedata = ""<br />
try:<br />
with open(self.storagepath, 'r') as fh:<br />
filedata = fh.read()<br />
except IOError as e:<br />
if os.path.isfile(self.storagepath):<br />
# Only error out if file exists, but cannot be read<br />
print("ERROR: Storage file exists but cannot be read")<br />
sys.exit(1)<br />
try:<br />
data = json.loads(filedata)<br />
except ValueError:<br />
if len(filedata) > 0:<br />
# Storage file is corrupted<br />
print("ERROR: Storage JSON is corrupted")<br />
sys.exit(1)<br />
return data<br />
<br />
def save(self):<br />
"""Saves the storage content to disk"""<br />
serialized = json.dumps(self._data)<br />
try:<br />
with os.fdopen(os.open(self.storagepath,<br />
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:<br />
fh.truncate()<br />
fh.write(serialized)<br />
except IOError as e:<br />
print("ERROR: Could not write storage file.")<br />
sys.exit(1)<br />
<br />
def put(self, key, value):<br />
"""Puts the configuration value to storage and sanitize it"""<br />
# If wildcard domain, remove the wildcard part as this will use the<br />
# same validation record name as the base domain<br />
if key.startswith("*."):<br />
key = key[2:]<br />
self._data[key] = value<br />
<br />
def fetch(self, key):<br />
"""Gets configuration value from storage"""<br />
try:<br />
return self._data[key]<br />
except KeyError:<br />
return None<br />
<br />
if __name__ == "__main__":<br />
# Init<br />
client = AcmeDnsClient(ACMEDNS_URL)<br />
storage = Storage(STORAGE_PATH)<br />
<br />
# Check if an account already exists in storage<br />
account = storage.fetch(DOMAIN)<br />
if FORCE_REGISTER or not account:<br />
# Create and save the new account<br />
account = client.register_account(ALLOW_FROM)<br />
storage.put(DOMAIN, account)<br />
storage.save()<br />
<br />
# Display the notification for the user to update the main zone<br />
msg = "Please add the following CNAME record to your main DNS zone:\n{}"<br />
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])<br />
print(msg.format(cname))<br />
<br />
# Update the TXT record in acme-dns instance<br />
client.update_txt_record(account, VALIDATION_TOKEN)<br />
<br />
# chmod +x /etc/letsencrypt/acme-dns-auth.py<br />
Usaremos a seguinte instrução para criar nosso certificado:<br />
# certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d doh.brasilpeeringforum.org<br />
Saving debug log to /var/log/letsencrypt/letsencrypt.log<br />
Plugins selected: Authenticator manual, Installer None<br />
Cert is due for renewal, auto-renewing...<br />
Renewing an existing certificate for doh.brasilpeeringforum.org<br />
Performing the following challenges:<br />
dns-01 challenge for doh.brasilpeeringforum.org<br />
Running manual-auth-hook command: /etc/letsencrypt/acme-dns-auth.py<br />
Output from manual-auth-hook command acme-dns-auth.py:<br />
Please add the following CNAME record to your main DNS zone:<br />
_acme-challenge.doh.brasilpeeringforum.org CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
<br />
Waiting for verification...<br />
<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Challenges loaded. Press continue to submit to CA. Pass "-v" for more info about<br />
challenges.<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Press Enter to Continue<br />
Nesse momento você cria o registro '''CNAME''' no seu DNS Autoritativo conforme ele solicitou: '''_acme-challenge.doh.brasilpeeringforum.org IN CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.''' e somente depois de criado e checado no DNS, você pressiona o '''Enter''' para continuar. Você pode checar dessa forma:<br />
# host -t cname _acme-challenge.doh.brasilpeeringforum.org<br />
_acme-challenge.doh.brasilpeeringforum.org is an alias for b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
Para que nosso certificado seja automaticamente renovado colocaremos no '''/etc/crontab''' a seguinte linha abaixo:<br />
00 00 1 * * root /usr/bin/certbot -q renew<br />
Acima temos a instrução para renovação automática do certificado. Repare que você vai precisar também copiar esse certificado para seus outros servidores, escolha um servidor para manter o certificado sempre atualizado e crie um script que faça a mesma cópia remotamente para os outros servidores. O '''scp''' e o '''rsync''' são seus aliados nisso.<br />
<br />
=== Configurando o Unbound ===<br />
Em nosso '''/etc/unbound/unbound.conf.d/local.conf''', adicionaremos no bloco "'''server:'''" o seguinte:<br />
interface: 10.10.10.10@443<br />
interface: 10.10.9.9@443<br />
tls-service-key: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/privkey.pem"<br />
tls-service-pem: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/fullchain.pem"<br />
Para usar o recurso do '''DoH''' você precisará habilitar o recurso no seu navegador e informar a URL. Vou colocar o exemplo do '''Google Chrome''': Digite '''chrome://settings/security?search=dns''' no seu Chrome e ative '''Usar DNS seguro''', selecione '''Personalizado''' e adicione nossa URL:<br />
[[Arquivo:Doh bpf2.png|nenhum|commoldura]]<br />
<br />
== Finalizando ==<br />
Aqui finalizamos nosso projeto para uma Rede de DNS(s) Recursivos Anycast com Hyperlocal. Esse projeto é escalável, seguro, resiliente e você entregará muito mais qualidade de Internet para o seu cliente. Pare de entregar o '''8.8.8.8''' para os seus clientes, você está contribuindo para uma Internet mais lenta, sem a qualidade que o seu cliente merece. Investi meu tempo, que é muito pouco, para deixar esse documento para a comunidade, para você melhorar o seu ISP, para dar um UP! nele, então vamos começar 2023 com o pé direito. O que acha?<br />
<br />
Como prova de conceito, uma imagem abaixo onde temos uma Rede em produção de DNS(s) Recursivos Anycast e apontando exatamente o momento em que houve alguma situação que fez com que as queries de DNS, convergissem de um node para outro, de forma transparente e automática para o cliente. Podemos notar também que ao ser resolvido o problema, o tráfego retornou para o seu node correto:<br />
[[Arquivo:Convergencia.png|nenhum|commoldura]]<br />
<br />
== KINDNS (Stands for Knowledge-Sharing and Instantiating Norms for DNS and Naming Security) ==<br />
Achou que havia terminado? Agora que você tem a capacidade de montar uma '''Rede de DNS Recursivo''' com todas essas features acima, com todas as ferramentas que foram comentadas, o que acha de certificar o que fez?<br />
<br />
Assim como o [https://www.manrs.org/ MANRS] veio para certificar nosso sistema de roteamento na Internet, agora temos o [https://kindns.org/ KINDNS] para certificar que nossos sistemas de DNS estão bem feitos e dentro dos padrões de segurança. Existem '''7 ações''' que podem ser certificadas para nossos DNS Recursivos e estão aqui em https://kindns.org/shared-private-resolvers/. Com essa nossa documentação, se bem aplicada, você pode se candidatar ao KINDNS e ter seu ASN listado aqui https://kindns.org/participants/<br />
<br />
Obter e manter o '''MANRS''' e '''KINDNS''' demonstra seu compromisso com as Boas Práticas, contribui para termos uma '''Internet''' mais segura e te abre portas para novos negócios que possam exigir essas conformidades.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]<br />
__FORCARTDC__</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Portas_de_Amplifica%C3%A7%C3%A3o_DDoS_e_Botnets&diff=3632Portas de Amplificação DDoS e Botnets2023-11-27T19:10:14Z<p>Gondim: </p>
<hr />
<div>{{DISPLAYTITLE:Portas de Amplificação DDoS e Botnets}}<br />
__TOC__<br />
[[Arquivo:Criminal-bandit-hacker-scaled-1.jpg|miniaturadaimagem|nenhum|459x459px]]<br />
<br><br />
==Introdução==<br />
Para contextualizar nosso artigo precisamos entender basicamente que: '''amplificação''' é quando perguntamos algo para algum serviço (porta) e a resposta retornada, é maior que o tamanho da pergunta. Tendo isso em mente, existem alguns '''serviços abertos na Internet''' com essa característica e são explorados para realizarem o que conhecemos como '''Ataques de''' '''DDoS (Distributed Denial of Service)'''. Sabem aqueles ataques que costumam derrubar nossa '''Operação de Internet''', causando indisponibilidade de acesso para nossos assinantes? São esses serviços mal configurados e abertos na Internet, que munem fortemente os cibercriminosos, que usam esses serviços abertos como ferramentas para esse propósito. Não se engane se você acha que na sua Rede, não possa existir esses serviços abertos e que ninguém estaria usando-os para executar esses ataques. Estamos acostumados a sempre reclamar de ataques recebidos, mas você já parou para pensar que você pode estar sendo usado como '''vetor de ataque''' e com isso, '''inconscientemente''', atacando alguém na Internet? Tirando o fato da imperícia de quem configurou erradamente o serviço, deixando-o '''vulnerável''', '''exposto''' e '''suscetível''' a ser explorado, essa pessoa é tanto vítima quanto você.<br />
<br />
== Consequências ==<br />
Quando somos usados como '''vetor de ataque''', nossa Rede se torna instável, nossos clientes reclamam de lentidão no acesso à Internet, nosso '''Call Center''' fica atarefado com solicitações que provavelmente eles não conseguirão identificar a causa raiz do problema. Como resultado teremos um aumento da insatisfação dos nossos clientes e muitos cancelamentos ('''churn'''). Para evitarmos todos esses males, resolvi escrever esse artigo dando dicas e explicando maneiras de fixarmos esses problemas.<br />
<br />
Quanto às '''Botnets,''' existem sistemas que foram comprometidos e podem estar sendo explorados para alguma atividade ilícita e que também poderá prejudicar a sua Operação de Internet. Esses casos também precisam ser tratados.<br />
<br />
==Portas de Amplificação e de Botnets mais conhecidas==<br />
{| class="wikitable"<br />
|+<br />
!'''Porta'''<br />
!Descrição<br />
|-<br />
|DNS (53/udp)<br />
|Servidores de DNS Recursivos abertos para o mundo. Não podemos confundir com o serviço de DNS Autoritativo (53/UDP/TCP), este precisa estar aberto à consultas dos domínios de sua autoridade.<br />
|-<br />
|SNMP (161/udp)<br />
|'''Simple Network Management Protocol''' ou como costumo brincar dizendo que o significado seria: '''Security is Not My Problem'''. Este serviço é muito usado para monitorarmos nossos ativos de Redes e Servidores.<br />
|-<br />
|NTP (123/udp)<br />
|'''Network Time Protocol''' é um serviço bastante importante para mantermos nossos sistemas com data e hora corretos.<br />
|-<br />
|SSDP (1900/udp)<br />
|'''Simple Service Discovery Protocol'''.<br />
|-<br />
|PORTMAP (111/udp)<br />
|Daemon que atribui portas dinamicamente para serviços '''RPC''' '''(Remote Procedure Call)''' como '''NIS (Network Information Service)''' e o '''NFS (Network File System)''' comumente usados em sistemas Unix Like.<br />
|-<br />
|NETBIOS (137/udp)<br />
|'''Network Basic Input/Output System''', faz parte dos serviços de compartilhamento de Redes baseadas em Microsoft Windows.<br />
|-<br />
|UBNT (10001/udp)<br />
|Serviço de '''Device Discovery''' habilitado nos equipamentos da Ubiquiti.<br />
|-<br />
|MDNS (5353/udp)<br />
|Multicast DNS.<br />
|-<br />
|LDAP (389/udp)<br />
|Serviço '''Lightweight Directory Access Protocol'''.<br />
|-<br />
|CHARGEN (19/udp)<br />
|'''Character Generator Protocol''', é um protocolo usado para fins de teste, depuração e medição. Esse serviço gera datagramas contendo um número aleatório de caracteres entre 0 e 252, sempre que requisitado.<br />
|-<br />
|QOTD (17/udp)<br />
|'''Quote Of The Day''' outro serviço de teste e medição.<br />
|-<br />
|MEMCACHED (11211/udp)<br />
|Serviço de cache para aceleração de aplicativos Web.<br />
|-<br />
|WS-DISCOVERY (3702/udp)<br />
|'''Web Services Dynamic Discovery Protocol''', é um protocolo de descoberta multicast para localizar serviços em uma Rede local.<br />
|-<br />
|TFTP (69/udp)<br />
|Trivial File Transfer Protocol, é um protocolo bastante utilizado por ativos de redes para por exemplo, transferir uma atualização de firmware.<br />
|-<br />
|CoAP (5683/udp)<br />
|'''Constrained Application Protocol''', é um protocolo de comunicação usado por dispositivos de Internet que possuem pouco recurso de hardware.<br />
|-<br />
|SLP (427/udp)<br />
|'''Service Location Protocol,''' é um protocolo que permite computadores e outros dispositivos encontrarem serviços em uma rede local sem a necessidade de configuração prévia.<br />
|-<br />
|ARMS (3283/udp)<br />
|'''Apple Remote Management Service''', serviço utilizado pela Apple para prover acesso remoto e gerenciamento de dispositivos rodando MacOS.<br />
|-<br />
|DHCPDiscover (37810/udp)<br />
|'''Dynamic Host Configuration Protocol Discover''', protocolo utilizado para gerenciar equipamentos DVR.<br />
|-<br />
|MT4145 (4145/tcp)<br />
|Serviço de SOCKS habilitado na porta 4145/tcp, normalmente visto em equipamentos Mikrotiks comprometidos e abusados por Botnet.<br />
|-<br />
|MT5678 (5678/tcp)<br />
|Serviço de SOCKS4 habilitado na porta 5678/tcp, visto em Mikrotiks comprometidos e sendo abusados pela Meris Botnet .<br />
|}<br />
<br />
== Como saber se temos Portas de Amplificação DDoS e de Botnets em nossa Rede? ==<br />
Primeiramente saiba que existem entidades que se preocupam com a segurança da '''Internet''' e que tem como objetivo extinguir a exposição desses serviços de amplificação da '''Internet'''. Uma entidade que podemos citar é o [https://www.shadowserver.org/news/the-scannings-will-continue-until-the-internet-improves/ '''ShadowServer'''] e a outra já conhecemos muito bem, que é o nosso valoroso [https://bcp.nic.br/i+seg/acoes/amplificacao/ '''CERT.br''']. Uma maneira bem fácil e simples de sermos reportados sobre esses problemas de segurança, é mantendo nosso '''Contato de Segurança''' atualizado no '''Registro.br'''. Mostrarei abaixo em telas de exemplo o quanto isso é fácil. Você precisará de ter a '''credencial de acesso do seu ASN''' no [https://registro.br/ '''Registro.br'''] e já ter criado um '''ID com seus dados''' e '''e-mail,''' que será usado pelo '''CERT.br''' para enviar as '''notificações de incidentes'''.<br />
[[Arquivo:Registrobr1.png|miniaturadaimagem|691x691px|esquerda]][[Arquivo:Registrobr6.png|miniaturadaimagem|691x691px|nenhum]]<br />
[[Arquivo:Registrobr3.png|miniaturadaimagem|691x691px|esquerda]][[Arquivo:Registrobr4.png|nenhum|miniaturadaimagem|691x691px]]<br />
[[Arquivo:Registrobr7.png|esquerda|miniaturadaimagem|691x691px]][[Arquivo:Registrobr5.png|miniaturadaimagem|691x691px|nenhum]]<br />
<br />
<br><br />
<br><br />
<br><br />
== O que vem depois ==<br />
Após acertar seu contato de segurança, você começará a receber e-mails do '''CERT.br''' e de '''outros ASNs''' sempre que houver algum incidente de segurança envolvendo o seu ASN e '''notificações''' sobre as '''portas de amplificação/botnets''', abertas em sua Rede, inclusive com cada e-mail referente a qual porta está aberta, com o esclarecimento técnico sobre os riscos e instruções de como testar e checar se o problema foi resolvido. Perceba que agora você não estará mais cego quanto aos problemas de segurança que sua Rede vem sofrendo e tenha certeza de que quanto mais limpa ela estiver, mais qualidade de serviço estará entregando ao seu cliente, diminuindo a quantidade de chamados em seu Call Center e consequentemente diminuindo seu '''churn'''.<br />
<br />
Um exemplo de e-mail que você receberá para cada serviço aberto:<br />
[[Arquivo:Certbr.png|nenhum|miniaturadaimagem|1122x1122px]]<br />
<br />
Até aqui te expliquei sobre o problema, o que ele pode causar ao seu negócio e te mostrei como passar a enxergá-los. Mas como tratá-los?<br />
<br />
== Tratando a causa raiz ==<br />
Como alguns devem ter percebido, nessa nossa relação de portas de amplificação existem portas que se forem bloqueadas te causarão dor de cabeça e muitas reclamações. Vou citar apenas como exemplo: '''DNS''', '''SNMP''' e '''NTP'''. Três serviços que se você não tiver cuidado, poderá impactar seus assinantes. Temos que entender que podemos ter 2 tipos de clientes: o '''residencial''' e o '''corporativo'''. O cliente residencial é mais simples de se resolver porque provavelmente ele não terá um servidor DNS rodando em sua residência, nem um SNMP e tão pouco um servidor NTP, porém o serviço de NTP não pode simplesmente ser bloqueado devido sua natureza de funcionamento e necessidade dos dispositivos, na residência, de manterem-se com a data e horário corretos.<br />
<br />
Já o cliente corporativo pode possuir todos esses serviços rodando e nesse caso faz-se necessário um maior entendimento junto ao cliente. O que quero dizer com isso é que para ambos os casos, você precisa elaborar um '''Plano de Ação''' para tratar cada caso, buscando sempre o resultado mais eficaz e sem causar indisponibilidade para seu cliente. O que vou propor nesse artigo são duas das formas de se tratar o problema, mas precisamos ter bom senso na hora da decisão e termos certeza de como implementar a solução tecnicamente.<br />
<br />
A informação contendo os IPs da nossa Rede que estão com as Portas de Amplificação abertas já temos em mãos, agora precisamos tratá-la. Tenho 2 propostas em mente:<br />
<br />
* Identificar os clientes com as falhas de segurança e contatá-los um a um, explicar sobre o problema, o que isso pode afetá-los e ajudá-los a resolver a situação. Essa seria a maneira mais correta, uma boa prática, porque implica em melhorar a cultura de segurança, fazendo todos entenderem sobre os riscos envolvidos e aumentando o senso de cuidado. Em contra partida é uma ação muito trabalhosa e dependendo da quantidade de clientes pode ser muito demorada.<br />
* Aplicar filtros e controles de acordo com o perfil do cliente. Para se ter um resultado mais rápido e efetivo podemos aplicar filtros em nossos '''BNGs''', bloqueando as Portas de Amplificação '''sempre no sentido Internet para o cliente''', mas com alguns cuidados que citarei ainda. Observando sempre os casos de clientes corporativos e que podem necessitar de um tratamento separado.<br />
<br />
== Estratégia de bloqueio das Portas de Amplificação/Botnet ==<br />
Faremos o bloqueio de todas as Portas de Amplificação citadas na tabela desse artigo, sempre no sentido Internet para o cliente. Isso é muito importante! O '''serviço NTP''' terá um tratamento diferenciado porque não podemos apenas bloquear o serviço, precisaremos fazer um '''rate-limit''' e uma '''condição especial''' para que seja feito o drop dos pacotes. Como a quantidade de vendors é grande e cada Provedor escolhe o melhor para a sua Operação, trouxe aqui como exemplo regras para '''Mikrotik RouterOS''', basta entender a lógica como vou explicar e procurar fazer o mesmo em seu equipamento, seja de qual fabricante for. Escolhi este vendor como exemplo pois além de ser muito utilizado no mercado, devido ao seu custo x benefício, será mais fácil de explicar o funcionamento. Entendendo como funciona, você pode implementar no seu Cisco, Huawei, Juniper, etc.<br />
<br />
== As regras de bloqueio no RouterOS ==<br />
/ip firewall raw<br />
add action=drop chain=prerouting comment="# BLOQUEIA NTP DE ORIGEM NAO NTP" dst-port=123 in-interface=!all-ppp protocol=udp src-port=!123<br />
add action=drop chain=prerouting comment="# DROP ABUSO NTP" limit=!2M,2M:bit protocol=udp src-port=123<br />
add action=drop chain=prerouting comment="# DROP PORTAS UDP DE AMPLIFICACAO" dst-port=53,161,1900,111,137,10001,5353,389 in-interface=!all-ppp protocol=udp<br />
add action=drop chain=prerouting comment="# DROP PORTAS UDP DE AMPLIFICACAO" dst-port=19,17,11211,3702,69,5683,3283,37810 in-interface=!all-ppp protocol=udp<br />
add action=drop chain=prerouting comment="# DROP PORTAS TCP BOTNET" dst-port=4145,5678 in-interface=!all-ppp protocol=tcp tcp-flags=syn<br />
<br />
Estamos fazendo os bloqueios na tabela '''raw''' por ocupar menos processamento do sistema e sabemos o quanto isso é importante no RouterOS. Essas regras estão sendo aplicadas em um BNG concentrador PPPoE onde:<br />
<br />
* Na primeira linha '''ADD''' estamos bloqueando qualquer pacote entrando por qualquer '''interface que''' '''não seja PPP''', ou seja, que viria da Internet sentido o cliente, que está atrás de uma interface do tipo PPP. Ainda analisando esse filtro, somente entraria nessa regra se a '''porta origem''' do pacote '''não for a 123/udp''' e com '''destino a porta''' '''123/udp'''. Temos que concordar que se um dispositivo do cliente solicitar um sincronismo com algum servidor NTP na Internet, este responderá através da porta 123/udp. Não deve chegar para o cliente uma resposta com origem diferente da porta 123/udp. Por que isso? Algumas implementações do serviço NTP disparam requisições com porta origem 123 ao invés de uma porta origem >= 1024.<br />
* Na segunda linha '''ADD''' estamos fazendo um '''rate-limit de até 2Mbps''' para qualquer pacote com a '''porta origem 123/udp''' e para '''qualquer destino'''. Para evitar que a exploração da vulnerabilidade do serviço cause excesso de tráfego na conexão.<br />
* A terceira linha '''ADD''' bloqueamos o restante das portas udp de amplificação mas com a seguinte condição: pacotes que entrem por qualquer '''interface que não seja PPP''', novamente definindo o sentido Internet para o cliente e que as portas de destino sejam a nossa lista restante de portas de amplificação.<br />
* Por último no quarto '''ADD''', bloqueamos as portas '''TCP de Botnet 4145 e 5678''', de pacotes que entrem por qualquer interface que não seja do tipo PPP, com destino elas mas com uma condição do '''pacote estar setado com a tcp flag SYN''', que indica um pedido de início de conexão. Se tem alguém na Internet tentando se conectar nessas portas TCP e o sistema por trás for um RouterOS, algo não está bem.<br />
<br />
== Checando se o problema foi resolvido ==<br />
Após aplicarmos os filtros, checaremos se os IPs estão com as portas de amplificação fechadas e para isso fiz um '''shell script em bash''', que usa alguns pacotes para validarmos o serviço. Como sempre estou usando para os nossos artigos a distribuição Debian GNU/Linux. Procure separar um ambiente para instalar esses pacotes e servir como base de testes. O ideal é que esteja de fora na Internet e sem qualquer bloqueio de Operadoras para esses testes poderem funcionar. Para usar o '''rpcinfo''' precisaremos do pacote '''rpcbind''' instalado, que após a instalação levantará o serviço rpc (111/udp) e na sequência iremos desabilitá-lo.<br />
# apt install nmap bind9-host bind9-dnsutils bsdextrautils netcat snmp samba-common-bin rpcbind ntp ldap-utils curl<br />
<br />
# systemctl stop rpcbind.service<br />
# systemctl stop rpcbind.socket<br />
# systemctl disable rpcbind.service<br />
# systemctl disable rpcbind.socket<br />
O script dei o nome de '''amplicacao.sh''' mas fica ao seu critério chamá-lo como quiser. Ele se encontra aqui no Github: '''https://github.com/gondimcodes/amplificacao'''<br />
<br />
Para usar é bem simples. No exemplo abaixo fiz um cheque no 8.8.8.8:<br />
[[Arquivo:Amplificacao.png|nenhum|miniaturadaimagem|654x654px]]<br />
Se você quiser checar apenas um serviço se está aberto, podemos fornecer após o IP, o nome do serviço queremos testar seguindo essa lista de opções:<br />
<br />
* netbios<br />
* rpc<br />
* arms<br />
* tftp<br />
* dns<br />
* mdns<br />
* ssdp<br />
* snmp<br />
* ntp<br />
* ldap<br />
* ubnt<br />
* chargen<br />
* qotd<br />
* memcached<br />
* ws-discovery<br />
* coap<br />
* mt4145<br />
* mt5678<br />
* dhcpdiscover<br />
<br />
Para fazer o teste de apenas um serviço, só observar o exemplo abaixo:<br />
[[Arquivo:Amplificacao2.png|nenhum|miniaturadaimagem|560x560px]]<br />
<br />
== Conclusão ==<br />
Agora que sabemos o que são as Portas de Amplificação DDoS, os riscos para a nossa Operação de Internet, sabemos como ter visibilidade do problema e como checar e tratar esse problema; você ainda vai continuar deixando isso te afetar ou vamos dar aquele UP! na sua Operação e entregar mais qualidade de serviço para seus assinantes?<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Portas_de_Amplifica%C3%A7%C3%A3o_DDoS_e_Botnets&diff=3631Portas de Amplificação DDoS e Botnets2023-11-27T19:08:10Z<p>Gondim: </p>
<hr />
<div>{{DISPLAYTITLE:Portas de Amplificação DDoS e Botnets}}<br />
__TOC__<br />
[[Arquivo:Criminal-bandit-hacker-scaled-1.jpg|miniaturadaimagem|nenhum|459x459px]]<br />
<br><br />
==Introdução==<br />
Para contextualizar nosso artigo precisamos entender basicamente que: '''amplificação''' é quando perguntamos algo para algum serviço (porta) e a resposta retornada, é maior que o tamanho da pergunta. Tendo isso em mente, existem alguns '''serviços abertos na Internet''' com essa característica e são explorados para realizarem o que conhecemos como '''Ataques de''' '''DDoS (Distributed Denial of Service)'''. Sabem aqueles ataques que costumam derrubar nossa '''Operação de Internet''', causando indisponibilidade de acesso para nossos assinantes? São esses serviços mal configurados e abertos na Internet, que munem fortemente os cibercriminosos, que usam esses serviços abertos como ferramentas para esse propósito. Não se engane se você acha que na sua Rede, não possa existir esses serviços abertos e que ninguém estaria usando-os para executar esses ataques. Estamos acostumados a sempre reclamar de ataques recebidos, mas você já parou para pensar que você pode estar sendo usado como '''vetor de ataque''' e com isso, '''inconscientemente''', atacando alguém na Internet? Tirando o fato da imperícia de quem configurou erradamente o serviço, deixando-o '''vulnerável''', '''exposto''' e '''suscetível''' a ser explorado, essa pessoa é tanto vítima quanto você.<br />
<br />
== Consequências ==<br />
Quando somos usados como '''vetor de ataque''', nossa Rede se torna instável, nossos clientes reclamam de lentidão no acesso à Internet, nosso '''Call Center''' fica atarefado com solicitações que provavelmente eles não conseguirão identificar a causa raiz do problema. Como resultado teremos um aumento da insatisfação dos nossos clientes e muitos cancelamentos ('''churn'''). Para evitarmos todos esses males, resolvi escrever esse artigo dando dicas e explicando maneiras de fixarmos esses problemas.<br />
<br />
Quanto às '''Botnets,''' existem sistemas que foram comprometidos e podem estar sendo explorados para alguma atividade ilícita e que também poderá prejudicar a sua Operação de Internet. Esses casos também precisam ser tratados.<br />
<br />
==Portas de Amplificação e de Botnets mais conhecidas==<br />
{| class="wikitable"<br />
|+<br />
!'''Porta'''<br />
!Descrição<br />
|-<br />
|DNS (53/udp)<br />
|Servidores de DNS Recursivos abertos para o mundo. Não podemos confundir com o serviço de DNS Autoritativo (53/UDP/TCP), este precisa estar aberto à consultas dos domínios de sua autoridade.<br />
|-<br />
|SNMP (161/udp)<br />
|'''Simple Network Management Protocol''' ou como costumo brincar dizendo que o significado seria: '''Security is Not My Problem'''. Este serviço é muito usado para monitorarmos nossos ativos de Redes e Servidores.<br />
|-<br />
|NTP (123/udp)<br />
|'''Network Time Protocol''' é um serviço bastante importante para mantermos nossos sistemas com data e hora corretos.<br />
|-<br />
|SSDP (1900/udp)<br />
|'''Simple Service Discovery Protocol'''.<br />
|-<br />
|PORTMAP (111/udp)<br />
|Daemon que atribui portas dinamicamente para serviços '''RPC''' '''(Remote Procedure Call)''' como '''NIS (Network Information Service)''' e o '''NFS (Network File System)''' comumente usados em sistemas Unix Like.<br />
|-<br />
|NETBIOS (137/udp)<br />
|'''Network Basic Input/Output System''', faz parte dos serviços de compartilhamento de Redes baseadas em Microsoft Windows.<br />
|-<br />
|UBNT (10001/udp)<br />
|Serviço de '''Device Discovery''' habilitado nos equipamentos da Ubiquiti.<br />
|-<br />
|MDNS (5353/udp)<br />
|Multicast DNS.<br />
|-<br />
|LDAP (389/udp)<br />
|Serviço '''Lightweight Directory Access Protocol'''.<br />
|-<br />
|CHARGEN (19/udp)<br />
|'''Character Generator Protocol''', é um protocolo usado para fins de teste, depuração e medição. Esse serviço gera datagramas contendo um número aleatório de caracteres entre 0 e 252, sempre que requisitado.<br />
|-<br />
|QOTD (17/udp)<br />
|'''Quote Of The Day''' outro serviço de teste e medição.<br />
|-<br />
|MEMCACHED (11211/udp)<br />
|Serviço de cache para aceleração de aplicativos Web.<br />
|-<br />
|WS-DISCOVERY (3702/udp)<br />
|'''Web Services Dynamic Discovery Protocol''', é um protocolo de descoberta multicast para localizar serviços em uma Rede local.<br />
|-<br />
|TFTP (69/udp)<br />
|Trivial File Transfer Protocol, é um protocolo bastante utilizado por ativos de redes para por exemplo, transferir uma atualização de firmware.<br />
|-<br />
|CoAP (5683/udp)<br />
|'''Constrained Application Protocol''', é um protocolo de comunicação usado por dispositivos de Internet que possuem pouco recurso de hardware.<br />
|-<br />
|SLP(427/udp)<br />
|'''Service Location Protocol,''' é um protocolo que permite computadores e outros dispositivos encontrarem serviços em uma rede local sem a necessidade de configuração prévia.<br />
|-<br />
|ARMS (3283/udp)<br />
|'''Apple Remote Management Service''', serviço utilizado pela Apple para prover acesso remoto e gerenciamento de dispositivos rodando MacOS.<br />
|-<br />
|DHCPDiscover (37810/udp)<br />
|'''Dynamic Host Configuration Protocol Discover''', protocolo utilizado para gerenciar equipamentos DVR.<br />
|-<br />
|MT4145 (4145/tcp)<br />
|Serviço de SOCKS habilitado na porta 4145/tcp, normalmente visto em equipamentos Mikrotiks comprometidos e abusados por Botnet.<br />
|-<br />
|MT5678 (5678/tcp)<br />
|Serviço de SOCKS4 habilitado na porta 5678/tcp, visto em Mikrotiks comprometidos e sendo abusados pela Meris Botnet .<br />
|}<br />
<br />
== Como saber se temos Portas de Amplificação DDoS e de Botnets em nossa Rede? ==<br />
Primeiramente saiba que existem entidades que se preocupam com a segurança da '''Internet''' e que tem como objetivo extinguir a exposição desses serviços de amplificação da '''Internet'''. Uma entidade que podemos citar é o [https://www.shadowserver.org/news/the-scannings-will-continue-until-the-internet-improves/ '''ShadowServer'''] e a outra já conhecemos muito bem, que é o nosso valoroso [https://bcp.nic.br/i+seg/acoes/amplificacao/ '''CERT.br''']. Uma maneira bem fácil e simples de sermos reportados sobre esses problemas de segurança, é mantendo nosso '''Contato de Segurança''' atualizado no '''Registro.br'''. Mostrarei abaixo em telas de exemplo o quanto isso é fácil. Você precisará de ter a '''credencial de acesso do seu ASN''' no [https://registro.br/ '''Registro.br'''] e já ter criado um '''ID com seus dados''' e '''e-mail,''' que será usado pelo '''CERT.br''' para enviar as '''notificações de incidentes'''.<br />
[[Arquivo:Registrobr1.png|miniaturadaimagem|691x691px|esquerda]][[Arquivo:Registrobr6.png|miniaturadaimagem|691x691px|nenhum]]<br />
[[Arquivo:Registrobr3.png|miniaturadaimagem|691x691px|esquerda]][[Arquivo:Registrobr4.png|nenhum|miniaturadaimagem|691x691px]]<br />
[[Arquivo:Registrobr7.png|esquerda|miniaturadaimagem|691x691px]][[Arquivo:Registrobr5.png|miniaturadaimagem|691x691px|nenhum]]<br />
<br />
<br><br />
<br><br />
<br><br />
== O que vem depois ==<br />
Após acertar seu contato de segurança, você começará a receber e-mails do '''CERT.br''' e de '''outros ASNs''' sempre que houver algum incidente de segurança envolvendo o seu ASN e '''notificações''' sobre as '''portas de amplificação/botnets''', abertas em sua Rede, inclusive com cada e-mail referente a qual porta está aberta, com o esclarecimento técnico sobre os riscos e instruções de como testar e checar se o problema foi resolvido. Perceba que agora você não estará mais cego quanto aos problemas de segurança que sua Rede vem sofrendo e tenha certeza de que quanto mais limpa ela estiver, mais qualidade de serviço estará entregando ao seu cliente, diminuindo a quantidade de chamados em seu Call Center e consequentemente diminuindo seu '''churn'''.<br />
<br />
Um exemplo de e-mail que você receberá para cada serviço aberto:<br />
[[Arquivo:Certbr.png|nenhum|miniaturadaimagem|1122x1122px]]<br />
<br />
Até aqui te expliquei sobre o problema, o que ele pode causar ao seu negócio e te mostrei como passar a enxergá-los. Mas como tratá-los?<br />
<br />
== Tratando a causa raiz ==<br />
Como alguns devem ter percebido, nessa nossa relação de portas de amplificação existem portas que se forem bloqueadas te causarão dor de cabeça e muitas reclamações. Vou citar apenas como exemplo: '''DNS''', '''SNMP''' e '''NTP'''. Três serviços que se você não tiver cuidado, poderá impactar seus assinantes. Temos que entender que podemos ter 2 tipos de clientes: o '''residencial''' e o '''corporativo'''. O cliente residencial é mais simples de se resolver porque provavelmente ele não terá um servidor DNS rodando em sua residência, nem um SNMP e tão pouco um servidor NTP, porém o serviço de NTP não pode simplesmente ser bloqueado devido sua natureza de funcionamento e necessidade dos dispositivos, na residência, de manterem-se com a data e horário corretos.<br />
<br />
Já o cliente corporativo pode possuir todos esses serviços rodando e nesse caso faz-se necessário um maior entendimento junto ao cliente. O que quero dizer com isso é que para ambos os casos, você precisa elaborar um '''Plano de Ação''' para tratar cada caso, buscando sempre o resultado mais eficaz e sem causar indisponibilidade para seu cliente. O que vou propor nesse artigo são duas das formas de se tratar o problema, mas precisamos ter bom senso na hora da decisão e termos certeza de como implementar a solução tecnicamente.<br />
<br />
A informação contendo os IPs da nossa Rede que estão com as Portas de Amplificação abertas já temos em mãos, agora precisamos tratá-la. Tenho 2 propostas em mente:<br />
<br />
* Identificar os clientes com as falhas de segurança e contatá-los um a um, explicar sobre o problema, o que isso pode afetá-los e ajudá-los a resolver a situação. Essa seria a maneira mais correta, uma boa prática, porque implica em melhorar a cultura de segurança, fazendo todos entenderem sobre os riscos envolvidos e aumentando o senso de cuidado. Em contra partida é uma ação muito trabalhosa e dependendo da quantidade de clientes pode ser muito demorada.<br />
* Aplicar filtros e controles de acordo com o perfil do cliente. Para se ter um resultado mais rápido e efetivo podemos aplicar filtros em nossos '''BNGs''', bloqueando as Portas de Amplificação '''sempre no sentido Internet para o cliente''', mas com alguns cuidados que citarei ainda. Observando sempre os casos de clientes corporativos e que podem necessitar de um tratamento separado.<br />
<br />
== Estratégia de bloqueio das Portas de Amplificação/Botnet ==<br />
Faremos o bloqueio de todas as Portas de Amplificação citadas na tabela desse artigo, sempre no sentido Internet para o cliente. Isso é muito importante! O '''serviço NTP''' terá um tratamento diferenciado porque não podemos apenas bloquear o serviço, precisaremos fazer um '''rate-limit''' e uma '''condição especial''' para que seja feito o drop dos pacotes. Como a quantidade de vendors é grande e cada Provedor escolhe o melhor para a sua Operação, trouxe aqui como exemplo regras para '''Mikrotik RouterOS''', basta entender a lógica como vou explicar e procurar fazer o mesmo em seu equipamento, seja de qual fabricante for. Escolhi este vendor como exemplo pois além de ser muito utilizado no mercado, devido ao seu custo x benefício, será mais fácil de explicar o funcionamento. Entendendo como funciona, você pode implementar no seu Cisco, Huawei, Juniper, etc.<br />
<br />
== As regras de bloqueio no RouterOS ==<br />
/ip firewall raw<br />
add action=drop chain=prerouting comment="# BLOQUEIA NTP DE ORIGEM NAO NTP" dst-port=123 in-interface=!all-ppp protocol=udp src-port=!123<br />
add action=drop chain=prerouting comment="# DROP ABUSO NTP" limit=!2M,2M:bit protocol=udp src-port=123<br />
add action=drop chain=prerouting comment="# DROP PORTAS UDP DE AMPLIFICACAO" dst-port=53,161,1900,111,137,10001,5353,389 in-interface=!all-ppp protocol=udp<br />
add action=drop chain=prerouting comment="# DROP PORTAS UDP DE AMPLIFICACAO" dst-port=19,17,11211,3702,69,5683,3283,37810 in-interface=!all-ppp protocol=udp<br />
add action=drop chain=prerouting comment="# DROP PORTAS TCP BOTNET" dst-port=4145,5678 in-interface=!all-ppp protocol=tcp tcp-flags=syn<br />
<br />
Estamos fazendo os bloqueios na tabela '''raw''' por ocupar menos processamento do sistema e sabemos o quanto isso é importante no RouterOS. Essas regras estão sendo aplicadas em um BNG concentrador PPPoE onde:<br />
<br />
* Na primeira linha '''ADD''' estamos bloqueando qualquer pacote entrando por qualquer '''interface que''' '''não seja PPP''', ou seja, que viria da Internet sentido o cliente, que está atrás de uma interface do tipo PPP. Ainda analisando esse filtro, somente entraria nessa regra se a '''porta origem''' do pacote '''não for a 123/udp''' e com '''destino a porta''' '''123/udp'''. Temos que concordar que se um dispositivo do cliente solicitar um sincronismo com algum servidor NTP na Internet, este responderá através da porta 123/udp. Não deve chegar para o cliente uma resposta com origem diferente da porta 123/udp. Por que isso? Algumas implementações do serviço NTP disparam requisições com porta origem 123 ao invés de uma porta origem >= 1024.<br />
* Na segunda linha '''ADD''' estamos fazendo um '''rate-limit de até 2Mbps''' para qualquer pacote com a '''porta origem 123/udp''' e para '''qualquer destino'''. Para evitar que a exploração da vulnerabilidade do serviço cause excesso de tráfego na conexão.<br />
* A terceira linha '''ADD''' bloqueamos o restante das portas udp de amplificação mas com a seguinte condição: pacotes que entrem por qualquer '''interface que não seja PPP''', novamente definindo o sentido Internet para o cliente e que as portas de destino sejam a nossa lista restante de portas de amplificação.<br />
* Por último no quarto '''ADD''', bloqueamos as portas '''TCP de Botnet 4145 e 5678''', de pacotes que entrem por qualquer interface que não seja do tipo PPP, com destino elas mas com uma condição do '''pacote estar setado com a tcp flag SYN''', que indica um pedido de início de conexão. Se tem alguém na Internet tentando se conectar nessas portas TCP e o sistema por trás for um RouterOS, algo não está bem.<br />
<br />
== Checando se o problema foi resolvido ==<br />
Após aplicarmos os filtros, checaremos se os IPs estão com as portas de amplificação fechadas e para isso fiz um '''shell script em bash''', que usa alguns pacotes para validarmos o serviço. Como sempre estou usando para os nossos artigos a distribuição Debian GNU/Linux. Procure separar um ambiente para instalar esses pacotes e servir como base de testes. O ideal é que esteja de fora na Internet e sem qualquer bloqueio de Operadoras para esses testes poderem funcionar. Para usar o '''rpcinfo''' precisaremos do pacote '''rpcbind''' instalado, que após a instalação levantará o serviço rpc (111/udp) e na sequência iremos desabilitá-lo.<br />
# apt install nmap bind9-host bind9-dnsutils bsdextrautils netcat snmp samba-common-bin rpcbind ntp ldap-utils curl<br />
<br />
# systemctl stop rpcbind.service<br />
# systemctl stop rpcbind.socket<br />
# systemctl disable rpcbind.service<br />
# systemctl disable rpcbind.socket<br />
O script dei o nome de '''amplicacao.sh''' mas fica ao seu critério chamá-lo como quiser. Ele se encontra aqui no Github: '''https://github.com/gondimcodes/amplificacao'''<br />
<br />
Para usar é bem simples. No exemplo abaixo fiz um cheque no 8.8.8.8:<br />
[[Arquivo:Amplificacao.png|nenhum|miniaturadaimagem|654x654px]]<br />
Se você quiser checar apenas um serviço se está aberto, podemos fornecer após o IP, o nome do serviço queremos testar seguindo essa lista de opções:<br />
<br />
* netbios<br />
* rpc<br />
* arms<br />
* tftp<br />
* dns<br />
* mdns<br />
* ssdp<br />
* snmp<br />
* ntp<br />
* ldap<br />
* ubnt<br />
* chargen<br />
* qotd<br />
* memcached<br />
* ws-discovery<br />
* coap<br />
* mt4145<br />
* mt5678<br />
* dhcpdiscover<br />
<br />
Para fazer o teste de apenas um serviço, só observar o exemplo abaixo:<br />
[[Arquivo:Amplificacao2.png|nenhum|miniaturadaimagem|560x560px]]<br />
<br />
== Conclusão ==<br />
Agora que sabemos o que são as Portas de Amplificação DDoS, os riscos para a nossa Operação de Internet, sabemos como ter visibilidade do problema e como checar e tratar esse problema; você ainda vai continuar deixando isso te afetar ou vamos dar aquele UP! na sua Operação e entregar mais qualidade de serviço para seus assinantes?<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Usu%C3%A1rio:Gondim&diff=3599Usuário:Gondim2023-10-20T23:21:31Z<p>Gondim: </p>
<hr />
<div>'''Marcelo Gondim da Cunha'''<br />
[[Arquivo:Gondim paisagem.jpg|esquerda|commoldura]]<br />
<br />
'''Contribuições e trabalhos:'''<br />
<br />
* Administração de Sistemas Unix-Like desde 1996.<br />
* Consultor na Conectiva S/A - Unidade Rio em 2000.<br />
** Autor do projeto TuxFrw - https://github.com/gondimcodes/tuxfrw e https://github.com/gondimcodes/tuxfrw-nft<br />
* Administração de sistemas BSD pela FreeBSD Brasil em 2010.<br />
* Direção do AS53135 - Nettel Telecomunicações entre 2003 e 2021 atingindo a marca de 41.000 assinantes. Gerando qualidade na entrega de serviços e implantando boas práticas como: <br />
<br />
a) IPv6.<br />
<br />
b) MANRS.<br />
<br />
c) RPKI.<br />
<br />
d) CPEs com firmware baseada na BCOP <nowiki>https://www.m3aawg.org/sites/default/files/lac-bcop-1-m3aawg-v1-portuguese-final.pdf</nowiki><br />
<br />
'''Palestras ministradas:'''<br />
<br />
* Semana da Informática UERJ 2002 - TuxFrw.<br />
* CONISLI 2003 - TuxFrw.<br />
* CONISLI 2004 - Fazendo compras com Gentoo Linux - Palestra sobre a distribuição Linux e suas ferramentas fantásticas.<br />
* CONISLI 2004 - OpenVPN - Palestra sobre como criar VPNs seguras com OpenVPN e diferenças entre ele e o IPSec.<br />
* CONISLI 2005 - SPoP (Security Point of Presence) com OpenVPN - Como utilizar túneis encriptados do OpenVPN para acessar a Internet de forma segura, onde quer que esteja.<br />
* SECOMP 2005 UNIFEI - TuxFrw.<br />
<br />
* Debconf19 2019 - [https://debconf19.debconf.org/talks/4-debian-na-vida-de-uma-operadora-de-telecom/ Debian na vida de uma operadora de Telecom], [https://www.youtube.com/watch?v=vQSTslUZy8k&list=PLYUtdmpYPTTJDtwgD8AtxzFJ9t_URhFMK&index=35 vídeo] e [https://salsa.debian.org/debconf-team/public/share/debconf19/raw/master/slides/4-debian-na-vida-de-uma-operadora-de-telecom.pdf?inline=false pdf].<br />
<br />
* [https://www.youtube.com/watch?v=5uOFtkplDts FiqueEmCasaUseDebian - CGNAT com NFTables] e [https://www.youtube.com/watch?v=Wz2IAg6MMlU SysAdmin apps].<br />
<br />
'''Artigos desenvolvidos para a comunidade do Brasil Peering Fórum:'''<br />
<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_na_pratica CGNAT na pratica].<br />
* [https://wiki.brasilpeeringforum.org/w/Acesso_via_IPv6_Link-Local Acesso via IPv6 Link-Local].<br />
* [https://wiki.brasilpeeringforum.org/w/MANRS MANRS].<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_Bulk_Port_Allocation_com_DPDK CGNAT Bulk Port Allocation com DPDK].<br />
* [https://wiki.brasilpeeringforum.org/w/Servidor_de_Logs Servidor de Logs].<br />
* [[DNS Recursivo Anycast Hyperlocal|DNS Anycast com Hyperlocal]].<br />
* [[Recomendações sobre Mitigação DDoS]]<br />
* [[Portas de Amplificação DDoS e Botnets]]<br />
* [[Static Loop - um erro que pode matar seu ISP/ITP]]<br />
* [[Identificando e neutralizando uma Botnet]]<br />
<br />
'''Tutorial:'''<br />
<br />
* [https://bit.ly/2saumHK Segurança de roteamento: MANRS (Mutually Agreed Norms for Routing Security) - Tutoriais NIC.br em 09/12/2019].<br />
<br />
'''[https://www.manrs.org/about/advisory-group/members/ MANRS Advisory Group Member (2020-2021)]'''.<br />
<br />
[https://www.youtube.com/watch?v=oahQkGx8urY '''Live sobre MANRS com Leonardo Furtado'''].<br />
<br />
Criação da Wiki ISPUP! em 24/12/2022.<br />
<br />
Semana de Capacitação 6 do NIC.br 28/04/2023 - '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT'''. Material [https://semanacap.bcp.nic.br/6-online/ aqui] e vídeo aula [https://www.youtube.com/watch?v=1q7J3NkQVSc aqui].<br />
<br />
'''Contatos:'''<br />
<br />
Telegram: '''@Marcelo_Gondim'''<br />
<br />
WhatsApp: +55 (22) 98827-9258<br />
<br />
E-mail: '''gondim at ispup.com.br'''<br />
<br />
Linkedin: https://www.linkedin.com/in/marcelo-gondim-sysadmin/<br />
<br />
Meu Github: https://github.com/gondimcodes<br />
<br />
== Mini-CV ==<br />
'''Marcelo Gondim''' começou sua carreira como desenvolvedor de software em COBOL e Clipper entre 1992 e 1995. Em 1996 foi responsável por desenvolver um sistema concorrente com o RENPAC da Embratel para acesso ao SISCOMEX e implantou a Internet para fins comerciais na empresa DATABRAS. Trabalhou como consultor e instrutor de Linux na Conectiva S/A em 2000. Em 2003 se tornou consultor de diversos Provedores de Internet na Região dos Lagos - RJ e onde acabou se tornando CTO da Nettel Telecomunicações (AS53135) com 42.000 assinantes. Implantou IPv6 iniciando em 2013 e se tornou participante do MANRS com diversas contribuições com artigos e palestras. Atualmente é Especialista em Redes, cuida do SOC (Security Operations Center) da Brasil TecPar AS262907, onde desenvolve as boas práticas, tratamentos de incidentes relacionados ao ASN e desenvolve as estratégias de Mitigação DDoS da empresa. Também desenvolveu uma Rede de DNS Recursivo Anycast espalhada pelo RS e MT/MS, também certificada KINDNS.</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Usu%C3%A1rio:Gondim&diff=3598Usuário:Gondim2023-10-20T23:20:41Z<p>Gondim: </p>
<hr />
<div>'''Marcelo Gondim da Cunha'''<br />
[[Arquivo:Gondim paisagem.jpg|esquerda|commoldura]]<br />
<br />
'''Contribuições e trabalhos:'''<br />
<br />
* Administração de Sistemas Unix-Like desde 1996.<br />
* Consultor na Conectiva S/A - Unidade Rio em 2000.<br />
** [https://tuxfrw.linuxinfo.com.br Autor do projeto TuxFrw - https://github.com/gondimcodes/tuxfrw e https://github.com/gondimcodes/tuxfrw-nft]<br />
* Administração de sistemas BSD pela FreeBSD Brasil em 2010.<br />
* Direção do AS53135 - Nettel Telecomunicações entre 2003 e 2021 atingindo a marca de 41.000 assinantes. Gerando qualidade na entrega de serviços e implantando boas práticas como: <br />
<br />
a) IPv6.<br />
<br />
b) MANRS.<br />
<br />
c) RPKI.<br />
<br />
d) CPEs com firmware baseada na BCOP <nowiki>https://www.m3aawg.org/sites/default/files/lac-bcop-1-m3aawg-v1-portuguese-final.pdf</nowiki><br />
<br />
'''Palestras ministradas:'''<br />
<br />
* Semana da Informática UERJ 2002 - TuxFrw.<br />
* CONISLI 2003 - TuxFrw.<br />
* CONISLI 2004 - Fazendo compras com Gentoo Linux - Palestra sobre a distribuição Linux e suas ferramentas fantásticas.<br />
* CONISLI 2004 - OpenVPN - Palestra sobre como criar VPNs seguras com OpenVPN e diferenças entre ele e o IPSec.<br />
* CONISLI 2005 - SPoP (Security Point of Presence) com OpenVPN - Como utilizar túneis encriptados do OpenVPN para acessar a Internet de forma segura, onde quer que esteja.<br />
* SECOMP 2005 UNIFEI - TuxFrw.<br />
<br />
* Debconf19 2019 - [https://debconf19.debconf.org/talks/4-debian-na-vida-de-uma-operadora-de-telecom/ Debian na vida de uma operadora de Telecom], [https://www.youtube.com/watch?v=vQSTslUZy8k&list=PLYUtdmpYPTTJDtwgD8AtxzFJ9t_URhFMK&index=35 vídeo] e [https://salsa.debian.org/debconf-team/public/share/debconf19/raw/master/slides/4-debian-na-vida-de-uma-operadora-de-telecom.pdf?inline=false pdf].<br />
<br />
* [https://www.youtube.com/watch?v=5uOFtkplDts FiqueEmCasaUseDebian - CGNAT com NFTables] e [https://www.youtube.com/watch?v=Wz2IAg6MMlU SysAdmin apps].<br />
<br />
'''Artigos desenvolvidos para a comunidade do Brasil Peering Fórum:'''<br />
<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_na_pratica CGNAT na pratica].<br />
* [https://wiki.brasilpeeringforum.org/w/Acesso_via_IPv6_Link-Local Acesso via IPv6 Link-Local].<br />
* [https://wiki.brasilpeeringforum.org/w/MANRS MANRS].<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_Bulk_Port_Allocation_com_DPDK CGNAT Bulk Port Allocation com DPDK].<br />
* [https://wiki.brasilpeeringforum.org/w/Servidor_de_Logs Servidor de Logs].<br />
* [[DNS Recursivo Anycast Hyperlocal|DNS Anycast com Hyperlocal]].<br />
* [[Recomendações sobre Mitigação DDoS]]<br />
* [[Portas de Amplificação DDoS e Botnets]]<br />
* [[Static Loop - um erro que pode matar seu ISP/ITP]]<br />
* [[Identificando e neutralizando uma Botnet]]<br />
<br />
'''Tutorial:'''<br />
<br />
* [https://bit.ly/2saumHK Segurança de roteamento: MANRS (Mutually Agreed Norms for Routing Security) - Tutoriais NIC.br em 09/12/2019].<br />
<br />
'''[https://www.manrs.org/about/advisory-group/members/ MANRS Advisory Group Member (2020-2021)]'''.<br />
<br />
[https://www.youtube.com/watch?v=oahQkGx8urY '''Live sobre MANRS com Leonardo Furtado'''].<br />
<br />
Criação da Wiki ISPUP! em 24/12/2022.<br />
<br />
Semana de Capacitação 6 do NIC.br 28/04/2023 - '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT'''. Material [https://semanacap.bcp.nic.br/6-online/ aqui] e vídeo aula [https://www.youtube.com/watch?v=1q7J3NkQVSc aqui].<br />
<br />
'''Contatos:'''<br />
<br />
Telegram: '''@Marcelo_Gondim'''<br />
<br />
WhatsApp: +55 (22) 98827-9258<br />
<br />
E-mail: '''gondim at ispup.com.br'''<br />
<br />
Linkedin: https://www.linkedin.com/in/marcelo-gondim-sysadmin/<br />
<br />
Meu Github: https://github.com/gondimcodes<br />
<br />
== Mini-CV ==<br />
'''Marcelo Gondim''' começou sua carreira como desenvolvedor de software em COBOL e Clipper entre 1992 e 1995. Em 1996 foi responsável por desenvolver um sistema concorrente com o RENPAC da Embratel para acesso ao SISCOMEX e implantou a Internet para fins comerciais na empresa DATABRAS. Trabalhou como consultor e instrutor de Linux na Conectiva S/A em 2000. Em 2003 se tornou consultor de diversos Provedores de Internet na Região dos Lagos - RJ e onde acabou se tornando CTO da Nettel Telecomunicações (AS53135) com 42.000 assinantes. Implantou IPv6 iniciando em 2013 e se tornou participante do MANRS com diversas contribuições com artigos e palestras. Atualmente é Especialista em Redes, cuida do SOC (Security Operations Center) da Brasil TecPar AS262907, onde desenvolve as boas práticas, tratamentos de incidentes relacionados ao ASN e desenvolve as estratégias de Mitigação DDoS da empresa. Também desenvolveu uma Rede de DNS Recursivo Anycast espalhada pelo RS e MT/MS, também certificada KINDNS.</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Recomenda%C3%A7%C3%B5es_sobre_Mitiga%C3%A7%C3%A3o_DDoS&diff=3569Recomendações sobre Mitigação DDoS2023-09-20T20:32:18Z<p>Gondim: </p>
<hr />
<div>{{DISPLAYTITLE:Recomendações sobre Mitigação DDoS}}<br />
__TOC__<br />
[[Arquivo:How-to-Tell-If-Your-Business-Is-Suffering-from-a-DDoS-Attack.jpg|miniaturadaimagem|442x442px|nenhum]]<br />
<br />
<br />
'''Objetivo''': ajudar você '''Provedor de Internet''', a diminuir os impactos causados por ataques DDoS em sua infraestrutura. Seguir algumas boas práticas vão te ajudar nesse processo mas não fazem mágica. Achar que colocar regras de filtros de pacotes no seu router de borda vai te salvar, é um ledo engano e você vai descobrir isso da pior maneira possível. Então preste bem a atenção no que vou te passar nesse artigo.<br />
<br />
== Técnicas e objetivos ==<br />
Em 2020 eu vivi e senti na pele ataques volumétricos, cheguei a receber ataques de 400Gbps durante 24h, 7 dias na semana e por 30 dias seguidos. O objetivo era saturar qualquer link e dar bastante trabalho para as nuvens de mitigação. Se somarmos isso com a falta de algumas coisas que poderíamos ter feito para proteção, antes que isso ocorresse, podem imaginar o caos que foi.<br />
<br />
Em 2022 percebi uma nova abordagem desses criminosos, já que muitos provedores já estavam preparados com sistemas de detecção e nuvens de mitigação. O ataque deixou de ser absurdamente volumétrico e passou a ser o conhecido '''Hit-and-Run'''. Os ataques tem pouca volumetria e são rápidos. O objetivo desse ataque é colocar o máximo de seus '''prefixos /24''' na mitigação e te causar problemas como:<br />
<br />
* Estourar a sua '''capacidade de clean pipe''' e '''burst''' causando lentidão nos acessos à Internet. O '''Clean Pipe''' seria o seu tráfego já limpo e mitigado pela sua '''fornecedora da Nuvem de Mitigação'''. É como um link de trânsito IP contratado e por isso tem limitação.<br />
* Te forçar a aumentar sua capacidade de '''clean pipe''' e '''burst''', causando um certo prejuízo ao Provedor, já que não é um serviço barato.<br />
* Todo prefixo mitigado causa efeitos colaterais para os clientes, não é perfeito porque não temos como testar os milhares serviços existentes na Internet e seu comportamento dentro da mitigação e por isso precisamos fazer ajustes. Isso causa, para o cliente que usa um serviço ainda não ajustado, a sensação de uma Internet com problemas.<br />
Agora em 2023 começaram novos ataques em diversos Provedores pelo Brasil e a técnica escolhida ainda é a '''Hit-and-Run''', resolvi escrever esse artigo para que você não espere acontecer contigo, fazer isso que vou colocar aqui durante eventos de DDoS é muito complicado e por isso temos que ser proativos nisso e não reativos.<br />
<br />
== Recomendações de prevenção ==<br />
As recomendações abaixo são muito importantes tanto para você se proteger, quanto para que não façam de você um vetor de ataque para outra redes.<br />
<br />
* Utilizar IPs privados ([https://www.rfc-editor.org/rfc/rfc1918 RFC1918]) nas sessões BGP com seus fornecedores de trânsito IP, para se evitar ataques diretos que derrubem as sessões BGP, causando 100% de indisponibilidade da Internet.<br />
* Use IPs privados ([https://www.rfc-editor.org/rfc/rfc1918 RFC1918]) em toda a sua rede de Gerência, nos Ps, PEs, uPEs, onde for possível. Isso bloqueia ataques diretos a eles.<br />
* Tenha equipamentos bem dimensionados e que não estejam operando quase no limite porque em eventos de DDoS, esses limites podem ser atingidos e causar a indisponibilidade da sua Operação.<br />
* Anunciar seus prefixos '''IPv4 /24''' para todo sistema de cache que tenha, como por exemplo: Google, Netflix, Facebook, Globo, etc. Os servidores de cache podem aliviar os clientes dos ataques e diminuir seu consumo de clean pipe. Se você ainda não tem um servidor de cache e tiver condições de ter, então peça. Você pode saber mais sobre isso nesse artigo: [[CDN Peering e PNI - Brasil|'''CDN Peering e PNI - Brasil''']]<br />
* '''PNIs''' confiáveis também podem ter seus /24 anunciados para eles, desde que sejam confiáveis. O '''PNI do Google''', por exemplo, por vender serviço de cloud podem vir ataques por lá.<br />
* Durante eventos de DDoS anunciar para o '''IX.br''' os '''/24''' atacados somente para '''Peerings Confiáveis'''. Você terá que analisar os participantes do IX um a um, ver quais teriam conteúdos importantes, mas que não ofereçam possibilidade de vir ataques deles. Basta usar a '''community 65001:XXXXX'''. Um lugar onde você pode ver sobre essas '''communities''': '''https://ix.br/route-server-and-commmunities'''<br />
* Implantar e utilizar IPv6 na operação. O tráfego IPv6 não é vítima de ataques DDoS e todos os acessos em IPv6 não sofrem com eventos DDoS, desde que a capacidade dos links de trânsito IP e CPU dos ativos de rede, não tenham sido comprometidos com os ataques.<br />
* Procure contratar Links de Trânsito IP que forneçam proteção DDoS. Isso pode ajudar também.<br />
* Comunicar e orientar seu atendimento para explicar ao cliente o que está ocorrendo e o que a empresa está fazendo para mitigar isso. Interagir também nas Redes Sociais junto aos clientes que estão sofrendo com os ataques e explicar o ocorrido.<br />
* Checar e eliminar os problemas de '''static loop''' na rede. Você pode ler sobre isso nesse meu artigo: '''[[Static Loop - um erro que pode matar seu ISP/ITP]]'''.<br />
* Resolver problemas com '''Portas de Amplificação DDoS''', Não seja também um vetor de ataques. Você pode ler sobre isso aqui: '''[[Portas de Amplificação DDoS e Botnets]]'''.<br />
* Tenha seus próprios servidores de DNS Recursivos e não use o 8.8.8.8, 1.1.1.1 ou outro externo porque em eventos de DDoS você vai ter problemas. Você pode fazer o seu seguindo esse meu artigo: [[DNS Recursivo Anycast Hyperlocal|'''DNS Recursivo Anycast HyperLoca'''l]].<br />
* Siga as Boas Práticas como se tonar um participante do '''[https://www.manrs.org/ MANRS]''' ou pelo menos executar as 4 ações. Você pode ver um vídeo e documentação sobre, aqui no '''Youtube''': '''https://www.youtube.com/watch?v=oahQkGx8urY'''<br />
* Juntar as provas dos ataques DDoS, como gráficos e qualquer outro indício dos mesmos e registrar um '''BO (Boletim de Ocorrência)''' na '''Delegacia de Crimes Cibernéticos''' mais perto do Provedor ou através de registros online. Abaixo exemplos de links do Rio de Janeiro e outro do Rio Grande do Sul. Não deixe de fazer o BO e vamos contribuir para a que a nossa Polícia consiga fazer o trabalho dela. Se não relatarmos isso, nada será feito. <br />
** RJ: https://delegaciaonline.pcivil.rj.gov.br <br />
** RS: https://www.delegaciaonline.rs.gov.br<br />
* Muito importante que você tenha um '''Sistema de Detecção e Mitigação DDoS''' e pelo menos uma '''Nuvem de Mitigação DDoS'''. Abaixo algumas empresas '''especializadas em DDoS''' e que podem fornecer esses serviços: <br />
** [https://sagenetworks.com.br/ Sage Networks].<br />
** [https://telic.com.br/ Telic].<br />
** [https://www.huge-networks.com/ Huge Networks].<br />
<br />
== Conclusão ==<br />
Essas são apenas algumas dicas valiosas para se proteger de DDoS. Não fique achando que colocar um filtro de pacotes no seu router de borda, vai te salvar de um '''ataque DDoS''', porque não vai. Além disso tudo que falei, existem muitas outras coisas que podemos fazer mas isso vou deixar para as empresas especializadas em DDoS trabalharem contigo e melhorar a sua vida durante esses eventos.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=DNS_Recursivo_Anycast_Hyperlocal&diff=3559DNS Recursivo Anycast Hyperlocal2023-07-26T12:34:22Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
==Introdução==<br />
Você sabe como funciona a Internet? Essa é uma pergunta que meu amigo '''Thiago Ayub''' sempre faz aos seus candidatos à vagas de emprego e não importa o quanto tenham de experiência em '''Engenharia de Redes''', todos sempre travam nesse momento. Todos estão sempre prontos e preparados para resolver os problemas mais cabeludos em '''BGP''', '''OSPF''', '''MPLS''', etc mas travam com essa simples pergunta. Para contextualizar e visualizarmos melhor vamos nos atentar à imagem abaixo e uma explicação simplificada de como funciona:<br />
[[Arquivo:Dns hierarquia.png|esquerda|commoldura]]<br />
Tudo começa com um usuário sentado confortavelmente e querendo acessar um conteúdo disponível na Internet. Ele digita em seu navegador preferido a URL: '''<nowiki>https://wiki.brasilpeeringforum.org</nowiki>''',<br />
<br />
'''<big>1)</big>''' <big>O</big> <big>navegador irá requisitar do '''DNS Recursivo''' utilizado pelo usuário, o '''endereço IP''' que responde pelo nome '''wiki.'''</big>'''brasilpeeringforum.org'''<big>. Isso porque todos os acessos se dão na Internet através do '''endereço''' '''IP''' e não através do '''nome'''. Imaginem se tivéssemos que decorar os endereços IPs de todos os sites e serviços que quiséssemos acessar na Internet?</big> <br />
<br />
<big>'''2)''' Nosso DNS Recursivo checa se a informação consta em seu cache.</big> Se a informação existir ela é devolvida ao navegador do usuário e aí este consegue acessar o site.<br />
<br />
'''3)''' Do contrário o DNS Recursivo pergunta ao '''Root Server''' quem é o '''TLD (Top Level Domain)''' responsável para atender a requisição. <br />
<br />
'''4)''' O '''Root Server''' informa ao DNS Recursivo o endereço do '''TLD responsável'''. No Brasil o '''TLD''' responsável pelo '''.br''' seria o '''Registro.br'''.<br />
<br />
'''5)''' O DNS Recursivo pergunta ao '''TLD''' sobre '''wiki.brasilpeeringforum.org''' e este responde com os endereços IP dos '''DNS Autoritativos''' responsáveis pelo domínio '''brasilpeeringforum.org.'''<br />
<br />
'''6)''' O DNS Recursivo pergunta aos '''DNS Autoritativos''' pelo '''wiki.brasilpeeringforum.org''' e este responde com o '''endereço IP'''.<br />
<br />
'''7)''' Por último o DNS Recursivo devolve a informação para o navegador do usuário.<br />
<br />
Como que se dá a comunicação entre os '''DNS(s) Recursivos, Root Servers, TLDs''' e '''Autoritativos'''? Como que o navegador do usuário, após receber o IP do site, consegue chegar no servidor que tem o conteúdo? Isso só é possível devido ao protocolo chamado '''BGP (Border Gateway Protocol)''', todos os caminhos que conhecemos como rotas de destino, são anunciadas por milhares de participantes na '''Internet''' conhecidos como '''AS (Autonomous System)''', esses participantes se interligam para disponibilizar conteúdos e acessos pelo mundo aos milhares de usuários. É uma imensa rede colaborativa formada por Empresas, Universidades, Governos e todos que queiram se interconectar. Percebam que sem o '''BGP''', que serve de caminho para chegarmos nos conteúdos e sem o '''DNS (Domain Name System)''' para traduzir o nome para o endereço IP, a '''Internet''' não funcionaria e por isso precisamos cuidar muito bem desses dois serviços.<br />
<br><br><br><br><br />
Mas não acaba por aí. O '''DNS Recursivo''' tem um papel muito importante para o Provedor de Internet e que envolve segurança, qualidade de acesso à Internet e a disponibilidade do serviço entregue ao cliente. Quando bem configurado acelera as consultas dos acessos graças ao seu cache interno, mas para que isso seja percebido pelo assinante, é necessário que esteja o mais próximo possível do seu cliente.<br />
<br><br />
== Um erro que destrói a qualidade do nosso serviço ==<br />
Um erro muito comum que muitas operadoras cometem é utilizar DNS Recursivo externo, como o '''8.8.8.8''', '''1.1.1.1''' e outros, para seus clientes. Quanto mais próximo dos seus clientes, mais qualidade de serviço estará entregando a eles. Conteúdos serão entregues mais rapidamente pois serão resolvidos e armazenados em caches locais e não consultados remotamente na Internet. Para falar mais sobre isso, te convido leitor desse documento, que assista essa palestra do '''Thiago Ayub''' no '''GTER 51/GTS 37''' (2022) '''8.888 MOTIVOS PARA NÃO USAR DNS RECURSIVO EXTERNO EM SEU AS''': https://www.youtube.com/watch?v=Rsvpu5uF2Io<br />
<br />
== Objetivo ==<br />
O objetivo desta documentação não é te ensinar tudo sobre '''DNS''', '''BGP''', '''OSPF''' e nem tão pouco sobre GNU/Linux e sim te mostrar um exemplo de servidor DNS Recursivo implementado pensando em segurança, qualidade e resiliência. Usaremos em todas as nossas documentações o [https://www.debian.org/ Debian GNU/Linux], por ser uma distribuição que considero uma obra de arte criada por uma enorme comunidade séria, com vasta experiência de anos, qualidade no empacotamento dos programas, estável e com uma equipe de segurança excelente e ativa. Caso você leitor, utilize alguma outra distribuição GNU/Linux, todo conteúdo apresentado aqui pode ser aplicado em outras distros, desde que respeitando as particularidades de cada uma.<br />
<br />
Aqui construiremos um sistema do tipo '''Anycast''', ou seja, terás o serviço rodando em diversas localidades da sua Rede utilizando o mesmo endereçamento IP e que atenderá seu cliente mais próximo. Em caso de falhas, seus clientes automaticamente e de forma transparente continuarão consultando o DNS mais próximo deles. Para que ele funcione dessa forma você precisará ter uma '''Rede OSPF''' implementada no seu Provedor Internet ou algum outro protocolo como por exemplo o '''ISIS,''' mas esse documento não irá abordar o '''ISIS'''. Também utilizaremos o '''Hyperlocal''' como recurso adicional para gerar algumas proteções de segurança e velocidade na resposta relacionada aos servidores de DNS Raiz da Internet.<br />
<br />
== Diagrama ==<br />
Para exemplificar nosso servidor de DNS Recursivo, usaremos como base das explicações um diagrama demonstrando o uso do DNS Recursivo em uma Rede fictícia. Adotaremos IPs privados e reservados para demonstrar todo o ambiente do Provedor de Internet.<br />
[[Arquivo:Diagrama dns recursivo.drawio.png|esquerda|commoldura]]<br />
Nesse diagrama podemos observar alguns detalhes técnicos como por exemplo: existem '''3 servidores de DNS Recursivo''' posicionados em locais diferentes, que poderiam estar em bairros diferentes e até em cidades diferentes. Em cada servidor teremos '''2 loopbacks''' com os IPs:<br />
<br />
'''10.10.10.10/32'''<br />
<br />
'''10.10.9.9/32'''<br />
<br />
Esses IPs serão entregues pelos concentradores '''PPPoE''' ou '''IPoE''' ('''BNG''') para seus clientes como '''DNS primário''' e '''secundário'''. Podemos usar IPs privados como DNS primário e secundário em um ambiente real? Sim podemos, desde que não sejam IPs que possam ter problemas com as redes privadas dos clientes. Ex.: rede do cliente usando '''192.168.0.0/24'''. Se entregarmos o DNS sendo '''192.168.0.10''' e '''192.168.0.20''' teremos problemas e o cliente ficará sem Internet, porque '''192.168.0.10''' e '''192.168.0.20''' fazem parte da rede '''192.168.0.0/24'''.<br />
<br />
Agora entregando '''10.10.10.10''' e '''10.10.9.9''' não teríamos problemas com a rede '''192.168.0.0/24'''.<br />
<br />
'''Motivos para usarmos IPs privados:'''<br />
* O principal motivo está relacionado com a segurança, uma vez que sendo um IP privado, não pode sofrer ataques DDoS direcionados diretamente para ele, vindos da Internet.<br />
* Nem mesmo o cliente da sua rede conhece os '''IPs públicos''' utilizados para recursividade na Internet.<br />
* Memorizar os IPs '''10.10.10.10''' e '''10.10.9.9''' é tão fácil quanto memorizar o '''8.8.8.8''' e o '''1.1.1.1'''. Mais fácil para o seu técnico guardar essa informação e utilizar onde for necessário.<br />
Cada servidor DNS Recursivo possui um '''IPv4 público''', aqui representado por '''198.18.x.x/27''' e um '''IPv6 global''' representado por um IP dentro do prefixo '''2001:db8::/32'''. Cada servidor precisa ter os seus próprios IPs e são através destes IPs que as consultas de DNS serão realizadas na Internet.<br />
<br />
Nessa topologia usando '''Anycast''', o cliente será sempre atendido pelo '''DNS Recursivo''' mais próximo, desde que os pesos no '''OSPF''' estejam ajustados corretamente.<br />
<br><br><br><br><br><br><br />
== Dados do servidor ==<br />
Podemos utilizar um sistema virtualizado ou não. Sistemas virtualizados são bem vindos pois são mais simples quando precisamos fazer backups, levantar outros sistemas sem complicações e se precisarmos restaurar rapidamente algum sistema que ficou indisponível por algum motivo. A configuração abaixo tem capacidade para atender algo em torno a '''50.000 assinantes ou mais'''. O DNS Recursivo é um serviço que pode ser utilizado até mesmo em um '''Raspberry Pi''' e atender operações pequenas, nesse caso com o intuito de economizar energia e espaço. Nosso foco aqui é montar uma rede de '''DNS Recursivo Anycast com HyperLocal'''. Como comentei acima o servidor deve ficar o mais próximo dos clientes para termos a '''menor latência possível''' e '''sempre menor que 5ms''' entre o cliente e o servidor.<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Memória<br />
!Disco<br />
!Sistema<br />
|-<br />
|2.4Ghz 6 cores<br />
|16G DDR4<br />
|30G<br />
|Debian 11 amd64 (Bullseye)<br />
|}<br />
<br />
== Softwares utilizados ==<br />
* Debian 11 amd64 (Bullseye) instalação mínima.<br />
<br />
* [https://frrouting.org/ FRRouting].<br />
* Unbound.<br />
* IRQBalance.<br />
* Chrony (NTP/NTS).<br />
* Shell script em bash.<br />
<br />
== Funcionalidades que teremos ==<br />
* Sistema em Anycast.<br />
* Hyperlocal.<br />
* Controle de acesso por <abbr>ACL</abbr>.<br />
* RPZ (Response Policy Zone).<br />
* Bloqueio de consultas do tipo ANY.<br />
* QNAME minimization habilitado. (habilitado por default no Unbound)<br />
* Recursividade em IPv4 e IPv6.<br />
* DNSSEC habilitado.<br />
* <abbr>DoH (DNS</abbr> over HTTPS) habilitado.<br />
<br />
== Monitoramento ==<br />
O monitoramento é algo bem específico e não é o foco deste documento mas é extremamente importante que você monitore seus servidores de DNS por alguma ferramenta como o Zabbix. Aqui mostrarei apenas como enviar as informações para o Zabbix. Algumas coisas que você deveria monitorar nos servidores de DNS Recursivo:<br />
* Serviço do unbound parou.<br />
* Perda de pacotes.<br />
* Latência alta de pacotes.<br />
* Lentidão na resolução de queries.<br />
* CPU alta.<br />
* Load alto.<br />
* Memória com uso alto.<br />
* Disco com pouco espaço.<br />
* Queda brusca nas queries.<br />
* A recursividade parou de funcionar.<br />
* A recursividade voltou a funcionar.<br />
Este abaixo é um exemplo de monitoramento de um sistema de DNS Recursivo que atende 50.000 assinantes:<br />
[[Arquivo:Grafana dns.png|nenhum|commoldura]]<br />
<br />
== Configurando a Rede ==<br />
Nossa documentação será baseada no diagrama apresentado acima e por isso configuraremos apenas um dos três servidores, porque os outros serão configurados da mesma forma, só que com dados diferentes. Para tanto assumirei que já temos um sistema Debian instalado com o mínimo de pacotes e somente com sshd, para que possamos acessar remotamente mais tarde. '''Não instale um ambiente gráfico no servidor''', você não deve querer fazer isso por diversos motivos e os principais: primeiro porque não é um Desktop e segundo porque o ambiente gráfico devoraria toda a memória com recursos que não seriam úteis aqui.<br />
<br />
Em '''/etc/network/interfaces''' deixaremos assim:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto lo:0<br />
iface lo:0 inet static<br />
address 10.10.10.10/32<br />
<br />
auto lo:1<br />
iface lo:1 inet static<br />
address 10.10.9.9/32<br />
<br />
# The primary network interface<br />
auto ens18<br />
iface ens18 inet static<br />
address 198.18.1.10/27<br />
gateway 198.18.1.1<br />
<br />
iface ens18 inet6 static<br />
address 2001:db8::faca:198:18:1:10/64<br />
gateway 2001:db8::faca:198:18:1:1<br />
<br />
# The secondary network interface<br />
auto ens18:0<br />
iface ens18:0 inet static<br />
address 172.16.0.6/30<br />
Nesse cenário temos as duas '''loopbacks''' com os IPs '''10.10.10.10''' e '''10.10.9.9''' que serão anunciados via OSPF para a rede e serem entregues aos clientes via BNG. Os IPs '''198.18.1.10''' e '''2001:db8::faca:198:18:1:10''' serão usados para fazerem a recursividade na Internet tanto em IPv4 quanto em IPv6. Esses IPs não devem ser divulgados para clientes; os IPs públicos são dedicados apenas para essa finalidade.<br />
<br />
== Configuração dos repositórios Debian ==<br />
Deixe o arquivo '''/etc/apt/sources.list''' conforme abaixo:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
Após a configuração vamos instalar alguns pacotes necessários e outros úteis:<br />
# apt update && apt full-upgrade<br />
# apt install net-tools nftables htop iotop sipcalc tcpdump curl gnupg rsync wget host dnsutils mtr-tiny bmon sudo tmux whois ethtool dnstop<br />
<br />
== Fazendo algum tuning no sistema ==<br />
Em '''/etc/sysctl.conf''' adicionamos no final do arquivo essas instruções:<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
Estamos fazendo algumas melhorias de memória, algumas relacionadas a '''conntrack''' porque se for usar um filtro de pacotes stateful, como o '''Netfilter/IPTables''' ou '''Netfilter/NFTables''', o valor default da tabela é pequeno e dependendo da situação, se estourar essa tabela, as consultas de DNS terão problemas também. O DNS Recursivo não deve ficar aberto para qualquer um na Internet, ele deve ser liberado apenas para seus clientes. Podemos fazer através das ACLs do Unbound e pelo filtro de pacotes. O último parâmetro diz respeito ao uso de swap, por padrão o Debian permite o uso de swap após 40% do uso da memória, nesse caso estamos dizendo para o sistema usar o swap com 90% de uso da memória.<br />
<br />
Precisamos adicionar o módulo '''nf_conntrack''' em '''/etc/modules''' para que seja carregado em tempo de boot, senão os parâmetros de '''conntrack''' que colocamos em '''/etc/sysctl.conf''' não serão carregados.<br />
# echo nf_conntrack >> /etc/modules<br />
# modprobe nf_conntrack<br />
# sysctl -p<br />
<br />
== Instalando o FRRouting ==<br />
O FRRouting é o programa que usaremos para fazer os anúncios das nossas loopbacks via OSPF. Nesse documento usaremos a versão 8.x e para isso precisaremos configurar o repositório oficial do FRRouting e instalar os pacotes:<br />
# echo "deb <nowiki>https://deb.frrouting.org/frr</nowiki> bullseye frr-8" > /etc/apt/sources.list.d/frr.list<br />
# curl -s <nowiki>https://deb.frrouting.org/frr/keys.asc</nowiki> | apt-key add -<br />
# apt update<br />
# apt install frr frr-doc frr-pythontools<br />
Aconselho depois de instalar os pacotes, marcá-los para não atualizar juntamente com os demais pacotes, isso é para evitar de ocorrer alguma atualização no FRRouting, que torne o serviço instável por algum motivo. Não que isso vá ocorrer, mas é melhor fazer essa atualização quando realmente for necessário.<br />
# apt-mark hold frr frr-doc frr-pythontools<br />
Após esse comando acima, o sistema manterá a instalação original do pacote intacta. Para desbloquear basta executar o comando abaixo:<br />
# apt-mark unhold frr frr-doc frr-pythontools<br />
<br />
== Removendo o APPARMOR ==<br />
O '''APPARMOR''' às vezes causa mais problemas que solução e se não for fazer uma completa configuração nele, é melhor desabilitá-lo. Para fazer isso efetivamente, o procedimento é esse abaixo:<br />
# mkdir -p /etc/default/grub.d<br />
# echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=0"' | tee /etc/default/grub.d/apparmor.cfg<br />
# update-grub<br />
# reboot<br />
<br />
== Instalando o Unbound ==<br />
Nesse momento ainda não iremos configurar o Unbound, apenas instalar o pacote e acertar o ambiente. Vamos instalar o unbound do backports porque este já possui suporte ao DoH que veremos mais à frente.<br />
# apt -t bullseye-backports install unbound<br />
# mkdir -p /var/log/unbound<br />
# touch /var/log/unbound/unbound.log<br />
# chown -R unbound:unbound /var/log/unbound/<br />
# cd /etc/unbound<br />
# wget -c <nowiki>ftp://FTP.INTERNIC.NET/domain/named.cache</nowiki><br />
# systemctl restart unbound<br />
Configurando o logrotate:<br />
cat << EOF > /etc/logrotate.d/unbound<br />
/var/log/unbound/unbound.log {<br />
rotate 5<br />
weekly<br />
postrotate<br />
unbound-control log_reopen<br />
endscript<br />
}<br />
EOF<br />
Reiniciando o serviço:<br />
# systemctl restart logrotate.service<br />
<br />
== Preparando o monitoramento do seu DNS Recursivo ==<br />
O monitoramento do seu DNS Recursivo é muito importante e para isso vamos usar um '''template para Zabbix''', que modifiquei juntamente com o seu shell script e que enviará os dados para o seu Zabbix server via '''zabbix-sender'''. O projeto original está aqui '''https://github.com/jeftedelima/Unbound-DNS''' e abaixo o template '''zbx_unbound_dns.xml''' modificado. Embora seja antigo é perfeitamente importável no Zabbix 6.0, por exemplo.<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<zabbix_export><br />
<version>3.2</version><br />
<date>2018-12-07T18:05:24Z</date><br />
<groups><br />
<group><br />
<name>02 - Servidores</name><br />
</group><br />
</groups><br />
<templates><br />
<template><br />
<template>Unbound - DNS</template><br />
<name>Unbound - DNS</name><br />
<description/><br />
<groups><br />
<group><br />
<name>02 - Servidores</name><br />
</group><br />
</groups><br />
<applications><br />
<application><br />
<name>Answer rcode</name><br />
</application><br />
<application><br />
<name>Query Type</name><br />
</application><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<items><br />
<item><br />
<name>Num answer nodata</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.answer.rcode.nodata</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Answer rcode</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num answer NOERROR</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.answer.rcode.NOERROR</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Answer rcode</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num answer NXDOMAIN</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.answer.rcode.NXDOMAIN</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Answer rcode</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num answer REFUSED</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.answer.rcode.REFUSED</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Answer rcode</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num answer SERVFAIL</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.answer.rcode.SERVFAIL</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Answer rcode</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num answer SECURE</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.answer.secure</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Answer rcode</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type A</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.a</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type AAAA</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.aaaa</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type MX</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.mx</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type NS</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.ns</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type PTR</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.ptr</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type SOA</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.soa</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type SRV</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.srv</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type TXT</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.txt</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type HTTPS</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.https</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type TYPE0</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.type0</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type CNAME</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.cname</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type WKS</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.wks</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type HINFO</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.hinfo</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type X25</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.x25</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type NAPTR</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.naptr</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type DS</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.ds</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type DNSKEY</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.dnskey</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type TLSA</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.tlsa</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type SVCB</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.svcb</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type SPF</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.spf</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type ANY</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.any</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Num query type OTHER</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>num.query.other</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Query Type</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total num cachehits</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.num.cachehits</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total num cachemiss</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.num.cachemiss</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total num prefetch</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.num.prefetch</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total num queries</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.num.queries</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total num recursive replies</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.num.recursivereplies</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total num requestlist avg</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.requestlist.avg</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>0</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total request current all</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.requestlist.current.all</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total request current user</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.requestlist.current.user</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total request exceeded</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.requestlist.exceeded</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total num requestlist max</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.requestlist.max</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total request overwritten</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.requestlist.overwritten</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
<item><br />
<name>Total tcp usage</name><br />
<type>2</type><br />
<snmp_community/><br />
<multiplier>0</multiplier><br />
<snmp_oid/><br />
<key>total.tcpusage</key><br />
<delay>0</delay><br />
<history>30</history><br />
<trends>30</trends><br />
<status>0</status><br />
<value_type>3</value_type><br />
<allowed_hosts/><br />
<units/><br />
<delta>0</delta><br />
<snmpv3_contextname/><br />
<snmpv3_securityname/><br />
<snmpv3_securitylevel>0</snmpv3_securitylevel><br />
<snmpv3_authprotocol>0</snmpv3_authprotocol><br />
<snmpv3_authpassphrase/><br />
<snmpv3_privprotocol>0</snmpv3_privprotocol><br />
<snmpv3_privpassphrase/><br />
<formula>1</formula><br />
<delay_flex/><br />
<params/><br />
<ipmi_sensor/><br />
<data_type>0</data_type><br />
<authtype>0</authtype><br />
<username/><br />
<password/><br />
<publickey/><br />
<privatekey/><br />
<port/><br />
<description/><br />
<inventory_link>0</inventory_link><br />
<applications><br />
<application><br />
<name>Requests</name><br />
</application><br />
</applications><br />
<valuemap/><br />
<logtimefmt/><br />
</item><br />
</items><br />
<discovery_rules/><br />
<httptests/><br />
<macros/><br />
<templates/><br />
<screens/><br />
</template><br />
</templates><br />
<graphs><br />
<graph><br />
<name>Answers rcode</name><br />
<width>900</width><br />
<height>200</height><br />
<yaxismin>0.0000</yaxismin><br />
<yaxismax>100.0000</yaxismax><br />
<show_work_period>1</show_work_period><br />
<show_triggers>1</show_triggers><br />
<type>0</type><br />
<show_legend>1</show_legend><br />
<show_3d>0</show_3d><br />
<percent_left>0.0000</percent_left><br />
<percent_right>0.0000</percent_right><br />
<ymin_type_1>0</ymin_type_1><br />
<ymax_type_1>0</ymax_type_1><br />
<ymin_item_1>0</ymin_item_1><br />
<ymax_item_1>0</ymax_item_1><br />
<graph_items><br />
<graph_item><br />
<sortorder>0</sortorder><br />
<drawtype>0</drawtype><br />
<color>1A7C11</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.answer.rcode.NOERROR</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>1</sortorder><br />
<drawtype>0</drawtype><br />
<color>F63100</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.answer.rcode.nodata</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>2</sortorder><br />
<drawtype>0</drawtype><br />
<color>2774A4</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.answer.rcode.NXDOMAIN</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>3</sortorder><br />
<drawtype>0</drawtype><br />
<color>A54F10</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.answer.rcode.REFUSED</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>4</sortorder><br />
<drawtype>0</drawtype><br />
<color>FC6EA3</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.answer.rcode.SERVFAIL</key><br />
</item><br />
</graph_item><br />
</graph_items><br />
</graph><br />
<graph><br />
<name>Informations</name><br />
<width>900</width><br />
<height>200</height><br />
<yaxismin>0.0000</yaxismin><br />
<yaxismax>100.0000</yaxismax><br />
<show_work_period>1</show_work_period><br />
<show_triggers>1</show_triggers><br />
<type>0</type><br />
<show_legend>1</show_legend><br />
<show_3d>0</show_3d><br />
<percent_left>0.0000</percent_left><br />
<percent_right>0.0000</percent_right><br />
<ymin_type_1>0</ymin_type_1><br />
<ymax_type_1>0</ymax_type_1><br />
<ymin_item_1>0</ymin_item_1><br />
<ymax_item_1>0</ymax_item_1><br />
<graph_items><br />
<graph_item><br />
<sortorder>0</sortorder><br />
<drawtype>0</drawtype><br />
<color>FC6EA3</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.requestlist.max</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>1</sortorder><br />
<drawtype>0</drawtype><br />
<color>CC0000</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.requestlist.avg</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>2</sortorder><br />
<drawtype>0</drawtype><br />
<color>6C59DC</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.requestlist.current.all</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>3</sortorder><br />
<drawtype>0</drawtype><br />
<color>AC8C14</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.requestlist.current.user</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>4</sortorder><br />
<drawtype>0</drawtype><br />
<color>611F27</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.requestlist.exceeded</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>5</sortorder><br />
<drawtype>0</drawtype><br />
<color>F230E0</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.requestlist.overwritten</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>6</sortorder><br />
<drawtype>0</drawtype><br />
<color>5CCD18</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.tcpusage</key><br />
</item><br />
</graph_item><br />
</graph_items><br />
</graph><br />
<graph><br />
<name>Query type</name><br />
<width>900</width><br />
<height>200</height><br />
<yaxismin>0.0000</yaxismin><br />
<yaxismax>100.0000</yaxismax><br />
<show_work_period>1</show_work_period><br />
<show_triggers>1</show_triggers><br />
<type>0</type><br />
<show_legend>1</show_legend><br />
<show_3d>0</show_3d><br />
<percent_left>0.0000</percent_left><br />
<percent_right>0.0000</percent_right><br />
<ymin_type_1>0</ymin_type_1><br />
<ymax_type_1>0</ymax_type_1><br />
<ymin_item_1>0</ymin_item_1><br />
<ymax_item_1>0</ymax_item_1><br />
<graph_items><br />
<graph_item><br />
<sortorder>0</sortorder><br />
<drawtype>0</drawtype><br />
<color>4AA09E</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.aaaa</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>1</sortorder><br />
<drawtype>0</drawtype><br />
<color>7DAAB9</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.mx</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>2</sortorder><br />
<drawtype>0</drawtype><br />
<color>CB641B</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.soa</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>3</sortorder><br />
<drawtype>0</drawtype><br />
<color>958936</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.srv</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>4</sortorder><br />
<drawtype>0</drawtype><br />
<color>8D6AAA</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.txt</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>5</sortorder><br />
<drawtype>0</drawtype><br />
<color>18E357</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.ns</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>6</sortorder><br />
<drawtype>0</drawtype><br />
<color>1390E4</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.a</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>7</sortorder><br />
<drawtype>0</drawtype><br />
<color>8F20D7</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.https</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>8</sortorder><br />
<drawtype>0</drawtype><br />
<color>117734</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.type0</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>9</sortorder><br />
<drawtype>0</drawtype><br />
<color>B722E9</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.cname</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>10</sortorder><br />
<drawtype>0</drawtype><br />
<color>3AF922</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.wks</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>11</sortorder><br />
<drawtype>0</drawtype><br />
<color>499E24</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.hinfo</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>12</sortorder><br />
<drawtype>0</drawtype><br />
<color>8B892D</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.x25</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>13</sortorder><br />
<drawtype>0</drawtype><br />
<color>DD4EDA</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.naptr</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>14</sortorder><br />
<drawtype>0</drawtype><br />
<color>8AD647</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.ds</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>15</sortorder><br />
<drawtype>0</drawtype><br />
<color>60908F</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.dnskey</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>16</sortorder><br />
<drawtype>0</drawtype><br />
<color>13F386</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.tlsa</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>17</sortorder><br />
<drawtype>0</drawtype><br />
<color>48A327</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.svcb</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>18</sortorder><br />
<drawtype>0</drawtype><br />
<color>D2EFD4</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.spf</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>19</sortorder><br />
<drawtype>0</drawtype><br />
<color>C437B5</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.any</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>20</sortorder><br />
<drawtype>0</drawtype><br />
<color>03B793</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>num.query.other</key><br />
</item><br />
</graph_item><br />
</graph_items><br />
</graph><br />
<graph><br />
<name>Total Queries</name><br />
<width>900</width><br />
<height>200</height><br />
<yaxismin>0.0000</yaxismin><br />
<yaxismax>100.0000</yaxismax><br />
<show_work_period>1</show_work_period><br />
<show_triggers>1</show_triggers><br />
<type>0</type><br />
<show_legend>1</show_legend><br />
<show_3d>0</show_3d><br />
<percent_left>0.0000</percent_left><br />
<percent_right>0.0000</percent_right><br />
<ymin_type_1>0</ymin_type_1><br />
<ymax_type_1>0</ymax_type_1><br />
<ymin_item_1>0</ymin_item_1><br />
<ymax_item_1>0</ymax_item_1><br />
<graph_items><br />
<graph_item><br />
<sortorder>0</sortorder><br />
<drawtype>0</drawtype><br />
<color>1A7C11</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.num.cachehits</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>1</sortorder><br />
<drawtype>0</drawtype><br />
<color>CC0000</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.num.cachemiss</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>2</sortorder><br />
<drawtype>0</drawtype><br />
<color>000000</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.num.queries</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>3</sortorder><br />
<drawtype>0</drawtype><br />
<color>A54F10</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.num.prefetch</key><br />
</item><br />
</graph_item><br />
<graph_item><br />
<sortorder>4</sortorder><br />
<drawtype>0</drawtype><br />
<color>0000EE</color><br />
<yaxisside>0</yaxisside><br />
<calc_fnc>2</calc_fnc><br />
<type>0</type><br />
<item><br />
<host>Unbound - DNS</host><br />
<key>total.num.recursivereplies</key><br />
</item><br />
</graph_item><br />
</graph_items><br />
</graph><br />
</graphs><br />
</zabbix_export><br />
Teremos um shell script que você precisará colocar no seu '''/etc/crontab'''. No exemplo abaixo assumi que o shell script está em '''/root/scripts'''. De 5 em 5 minutos os dados serão enviados para o seu Zabbix server.<br />
*/5 * * * * root /root/scripts/unboundSend.sh '''IP_zabbix_server''' '''nome_do_host''' 1> /dev/null<br />
Na linha acima, troque o '''IP_zabbix_server''' pelo '''IP do seu servidor Zabbix''' e o '''nome_do_host''' pelo '''hostname''' '''do seu DNS Recursivo'''. Você precisará instalar o pacote '''zabbix-sender''' no seu DNS Recursivo pois ele será usado para enviar os dados para o Zabbix server.<br />
<br />
Abaixo o '''unboundSend.sh''' também alterado com inclusão de mais dados:<br />
#!/bin/bash<br />
# @Jefte de Lima Ferreira<br />
# jeftedelima at gmail dot com<br />
# CRON Example<br />
# Contributor: Marcelo Gondim - gondim at gmail dot com<br />
# */5 **** root sh /home/dir/unboundSend.sh 192.168.10.1 Unbound 1> /dev/null<br />
<br />
if [ -z ${1} ] || [ -z ${2} ] ; then<br />
echo "You need to specify the IP address of zabbix server and hostname of your DNS Unbound on zabbix"<br />
echo "Usage example: ./unboundSend.sh 192.168.10.1 UnboundServer"<br />
exit 1<br />
fi<br />
<br />
# ZABBIX_SERVER IP<br />
IP_ZABBIX=$1<br />
# NAME Unbound on Zabbix<br />
NAME_HOST=$2<br />
DIR_TEMP=/var/tmp/<br />
FILE="${DIR_TEMP}dump_unbound_control_stats.txt"<br />
unbound-control stats > ${FILE}<br />
<br />
TOTAL_NUM_QUERIES=$(cat ${FILE} | grep -w 'total.num.queries' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEHITS=$(cat ${FILE} | grep -w 'total.num.cachehits' | cut -d '=' -f2)<br />
TOTAL_NUM_CACHEMISS=$(cat ${FILE} | grep -w 'total.num.cachemiss' | cut -d '=' -f2)<br />
TOTAL_NUM_PREFETCH=$(cat ${FILE} | grep -w 'total.num.prefetch' | cut -d '=' -f2)<br />
TOTAL_NUM_RECURSIVEREPLIES=$(cat ${FILE} | grep -w 'total.num.recursivereplies' | cut -d '=' -f2)<br />
<br />
TOTAL_REQ_MAX=$(cat ${FILE} | grep -w 'total.requestlist.max' | cut -d '=' -f2)<br />
TOTAL_REQ_AVG=$(cat ${FILE} | grep -w 'total.requestlist.avg' | cut -d '=' -f2)<br />
TOTAL_REQ_OVERWRITTEN=$(cat ${FILE} | grep -w 'total.requestlist.overwritten' | cut -d '=' -f2)<br />
TOTAL_REQ_EXCEEDED=$(cat ${FILE} | grep -w 'total.requestlist.exceeded' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_ALL=$(cat ${FILE} | grep -w 'total.requestlist.current.all' | cut -d '=' -f2)<br />
TOTAL_REQ_CURRENT_USER=$(cat ${FILE} | grep -w 'total.requestlist.current.user' | cut -d '=' -f2)<br />
<br />
TOTAL_TCPUSAGE=$(cat ${FILE} | grep -w 'total.tcpusage' | cut -d '=' -f2)<br />
<br />
NUM_QUERY_TYPE_A=$(cat ${FILE} | grep -w 'num.query.type.A' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NS=$(cat ${FILE} | grep -w 'num.query.type.NS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_MX=$(cat ${FILE} | grep -w 'num.query.type.MX' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TXT=$(cat ${FILE} | grep -w 'num.query.type.TXT' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_PTR=$(cat ${FILE} | grep -w 'num.query.type.PTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_AAAA=$(cat ${FILE} | grep -w 'num.query.type.AAAA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SRV=$(cat ${FILE} | grep -w 'num.query.type.SRV' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SOA=$(cat ${FILE} | grep -w 'num.query.type.SOA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HTTPS=$(cat ${FILE} | grep -w 'num.query.type.HTTPS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TYPE0=$(cat ${FILE} | grep -w 'num.query.type.TYPE0' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_CNAME=$(cat ${FILE} | grep -w 'num.query.type.CNAME' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_WKS=$(cat ${FILE} | grep -w 'num.query.type.WKS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_HINFO=$(cat ${FILE} | grep -w 'num.query.type.HINFO' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_X25=$(cat ${FILE} | grep -w 'num.query.type.X25' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_NAPTR=$(cat ${FILE} | grep -w 'num.query.type.NAPTR' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DS=$(cat ${FILE} | grep -w 'num.query.type.DS' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_DNSKEY=$(cat ${FILE} | grep -w 'num.query.type.DNSKEY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_TLSA=$(cat ${FILE} | grep -w 'num.query.type.TLSA' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SVCB=$(cat ${FILE} | grep -w 'num.query.type.SVCB' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_SPF=$(cat ${FILE} | grep -w 'num.query.type.SPF' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_ANY=$(cat ${FILE} | grep -w 'num.query.type.ANY' | cut -d '=' -f2)<br />
NUM_QUERY_TYPE_OTHER=$(cat ${FILE} | grep -w 'num.query.type.other' | cut -d '=' -f2)<br />
<br />
NUM_ANSWER_RCODE_NOERROR=$(cat ${FILE} | grep -w 'num.answer.rcode.NOERROR' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_NXDOMAIN=$(cat ${FILE} | grep -w 'num.answer.rcode.NXDOMAIN' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_SERVFAIL=$(cat ${FILE} | grep -w 'num.answer.rcode.SERVFAIL' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_REFUSED=$(cat ${FILE} | grep -w 'num.answer.rcode.REFUSED' | cut -d '=' -f2)<br />
NUM_ANSWER_RCODE_nodata=$(cat ${FILE} | grep -w 'num.answer.rcode.nodata' | cut -d '=' -f2)<br />
NUM_ANSWER_secure=$(cat ${FILE} | grep -w 'num.answer.secure' | cut -d '=' -f2)<br />
<br />
# Sending info to zabbix_server, if variables is not empty!<br />
[ -z ${TOTAL_NUM_QUERIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.queries -o ${TOTAL_NUM_QUERIES}<br />
[ -z ${TOTAL_NUM_CACHEHITS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachehits -o ${TOTAL_NUM_CACHEHITS}<br />
[ -z ${TOTAL_NUM_CACHEMISS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.cachemiss -o ${TOTAL_NUM_CACHEMISS}<br />
[ -z ${TOTAL_NUM_PREFETCH} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.prefetch -o ${TOTAL_NUM_PREFETCH}<br />
[ -z ${TOTAL_NUM_RECURSIVEREPLIES} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.num.recursivereplies -o ${TOTAL_NUM_RECURSIVEREPLIES}<br />
<br />
[ -z ${TOTAL_REQ_MAX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.max -o ${TOTAL_REQ_MAX}<br />
[ -z ${TOTAL_REQ_AVG} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.avg -o ${TOTAL_REQ_AVG}<br />
[ -z ${TOTAL_REQ_OVERWRITTEN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.overwritten -o ${TOTAL_REQ_OVERWRITTEN}<br />
[ -z ${TOTAL_REQ_EXCEEDED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.exceeded -o ${TOTAL_REQ_EXCEEDED}<br />
[ -z ${TOTAL_REQ_CURRENT_ALL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.all -o ${TOTAL_REQ_CURRENT_ALL}<br />
[ -z ${TOTAL_REQ_CURRENT_USER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.requestlist.current.user -o ${TOTAL_REQ_CURRENT_USER}<br />
<br />
[ -z ${TOTAL_TCPUSAGE} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k total.tcpusage -o ${TOTAL_TCPUSAGE}<br />
<br />
[ -z ${NUM_QUERY_TYPE_A} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.a -o ${NUM_QUERY_TYPE_A}<br />
[ -z ${NUM_QUERY_TYPE_NS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ns -o ${NUM_QUERY_TYPE_NS}<br />
[ -z ${NUM_QUERY_TYPE_MX} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.mx -o ${NUM_QUERY_TYPE_MX}<br />
[ -z ${NUM_QUERY_TYPE_TXT} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.txt -o ${NUM_QUERY_TYPE_TXT}<br />
[ -z ${NUM_QUERY_TYPE_PTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ptr -o ${NUM_QUERY_TYPE_PTR}<br />
[ -z ${NUM_QUERY_TYPE_AAAA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.aaaa -o ${NUM_QUERY_TYPE_AAAA}<br />
[ -z ${NUM_QUERY_TYPE_SRV} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.srv -o ${NUM_QUERY_TYPE_SRV}<br />
[ -z ${NUM_QUERY_TYPE_SOA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.soa -o ${NUM_QUERY_TYPE_SOA}<br />
[ -z ${NUM_QUERY_TYPE_HTTPS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.https -o ${NUM_QUERY_TYPE_HTTPS}<br />
[ -z ${NUM_QUERY_TYPE_TYPE0} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.type0 -o ${NUM_QUERY_TYPE_TYPE0}<br />
[ -z ${NUM_QUERY_TYPE_CNAME} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.cname -o ${NUM_QUERY_TYPE_CNAME}<br />
[ -z ${NUM_QUERY_TYPE_WKS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.wks -o ${NUM_QUERY_TYPE_WKS}<br />
[ -z ${NUM_QUERY_TYPE_HINFO} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.hinfo -o ${NUM_QUERY_TYPE_HINFO}<br />
[ -z ${NUM_QUERY_TYPE_X25} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.X25 -o ${NUM_QUERY_TYPE_X25}<br />
[ -z ${NUM_QUERY_TYPE_NAPTR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.naptr -o ${NUM_QUERY_TYPE_NAPTR}<br />
[ -z ${NUM_QUERY_TYPE_DS} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.ds -o ${NUM_QUERY_TYPE_DS}<br />
[ -z ${NUM_QUERY_TYPE_DNSKEY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.dnskey -o ${NUM_QUERY_TYPE_DNSKEY}<br />
[ -z ${NUM_QUERY_TYPE_TLSA} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.tlsa -o ${NUM_QUERY_TYPE_TLSA}<br />
[ -z ${NUM_QUERY_TYPE_SVCB} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.svcb -o ${NUM_QUERY_TYPE_SVCB}<br />
[ -z ${NUM_QUERY_TYPE_SPF} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.spf -o ${NUM_QUERY_TYPE_SPF}<br />
[ -z ${NUM_QUERY_TYPE_ANY} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.any -o ${NUM_QUERY_TYPE_ANY}<br />
[ -z ${NUM_QUERY_TYPE_OTHER} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.query.other -o ${NUM_QUERY_TYPE_OTHER}<br />
<br />
[ -z ${NUM_ANSWER_RCODE_NOERROR} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NOERROR -o ${NUM_ANSWER_RCODE_NOERROR}<br />
[ -z ${NUM_ANSWER_RCODE_NXDOMAIN} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.NXDOMAIN -o ${NUM_ANSWER_RCODE_NXDOMAIN}<br />
[ -z ${NUM_ANSWER_RCODE_SERVFAIL} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.SERVFAIL -o ${NUM_ANSWER_RCODE_SERVFAIL}<br />
[ -z ${NUM_ANSWER_RCODE_REFUSED} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.REFUSED -o ${NUM_ANSWER_RCODE_REFUSED}<br />
[ -z ${NUM_ANSWER_RCODE_nodata} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.rcode.nodata -o ${NUM_ANSWER_RCODE_nodata}<br />
[ -z ${NUM_ANSWER_secure} ] || zabbix_sender -z ${IP_ZABBIX} -s ${NAME_HOST} -k num.answer.secure -o ${NUM_ANSWER_secure}<br />
No Zabbix será registrado dados como esses abaixo e posteriormente pode ser montado um Grafana com eles:<br />
[[Arquivo:Zabbix dns01.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns02.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns03.png|nenhum|commoldura]]<br />
[[Arquivo:Zabbix dns04.png|nenhum|commoldura]]<br />
<br />
== Balanceando o processamento e mantendo a hora certa ==<br />
Vamos instalar 2 programas agora, o IRQBalance e o Chrony. O primeiro para balancear a carga entre os cores e o segundo para manter a data e hora certas no sistema:<br />
# apt install irqbalance<br />
# systemctl enable irqbalance<br />
# apt install chrony<br />
Após a instalação do Chrony edite o arquivo /etc/chrony/chrony.conf, comente e a linha abaixo e adicione seus servidores NTP. Caso não tenha servidores NTP, estou colocando os do NIC.br aqui.<br />
#pool 2.debian.pool.ntp.org iburst<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
<br />
# systemctl restart chronyd.service<br />
Cheque com o '''chronyc''' se os servidores estão OK:<br />
# chronyc sourcestats<br />
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev<br />
==============================================================================<br />
a.st1.ntp.br 10 5 155m -0.027 0.030 -71us 51us<br />
b.st1.ntp.br 11 7 344m +0.068 0.079 +23ms 382us<br />
c.st1.ntp.br 6 3 344m +0.026 0.037 -124us 92us<br />
200.20.186.76 9 3 138m -0.022 0.031 +172us 42us<br />
<br />
# chronyc sources<br />
MS Name/IP address Stratum Poll Reach LastRx Last sample<br />
===============================================================================<br />
^* a.st1.ntp.br 1 10 377 588 +487us[ +397us] +/- 12ms<br />
^- b.st1.ntp.br 2 10 377 830 +23ms[ +23ms] +/- 49ms<br />
^+ c.st1.ntp.br 2 10 21 1038 -147us[ -242us] +/- 17ms<br />
^+ 200.20.186.76 1 10 377 1032 +381us[ +285us] +/- 15ms<br />
<br />
== Configurando o FRRouting ==<br />
Nesse ponto iremos configurar o '''FRRouting''' para enviar os IPs das '''loopbacks''' e o '''/30''' para o nosso PE do diagrama. Em '''/etc/frr/daemons''' habilite o parâmetro conforme abaixo:<br />
ospfd=yes<br />
Edite o arquivo '''/etc/frr/frr.conf''' e deixe com o conteúdo abaixo, para ficar conforme nosso diagrama do projeto. Apenas troque '''<SENHA>''' por uma senha para fechar o OSPF com mais segurança. Essa senha deve ser usada dos dois lados.<br />
frr version 8.2.2<br />
frr defaults traditional<br />
hostname dns-recursivo-01<br />
log syslog informational<br />
no ip forwarding<br />
no ipv6 forwarding<br />
service integrated-vtysh-config<br />
!<br />
interface ens18<br />
ip ospf message-digest-key 5 md5 '''<SENHA>'''<br />
ip ospf network point-to-point<br />
exit<br />
!<br />
router ospf<br />
ospf router-id 172.16.0.6<br />
network 10.10.10.10/32 area 0.0.0.0<br />
network 10.10.9.9/32 area 0.0.0.0<br />
network 172.16.0.4/30 area 0.0.0.0<br />
area 0 authentication message-digest<br />
exit<br />
!<br />
<br />
# systemctl restart frr.service<br />
Cheque se está tudo OK com o OSPF e verifique no PE se está recebendo os prefixos anunciados.<br />
# vtysh -c 'show ip ospf neighbor'<br />
<br />
Neighbor ID Pri State Up Time Dead Time Address Interface RXmtL RqstL DBsmL<br />
172.16.0.5 1 Full/- 10m49s 35.310s 172.16.0.5 ens18:172.16.0.6 0 0 0<br />
<br />
# vtysh -c 'show ip ospf neighbor detail'<br />
<br />
Neighbor 172.16.0.5, interface address 172.16.0.5<br />
In the area 0.0.0.0 via interface ens18<br />
Neighbor priority is 1, State is Full/-, 5 state changes<br />
Most recent state change statistics:<br />
Progressive change 21w3d15h ago<br />
DR is 0.0.0.0, BDR is 0.0.0.0<br />
Options 18 *|-|-|EA|-|-|E|-<br />
Dead timer due in 34.685s<br />
Database Summary List 0<br />
Link State Request List 0<br />
Link State Retransmission List 0<br />
Thread Inactivity Timer on<br />
Thread Database Description Retransmision off<br />
Thread Link State Request Retransmission on<br />
Thread Link State Update Retransmission on<br />
<br />
Graceful restart Helper info:<br />
Graceful Restart HELPER Status : None<br />
<br />
== Configurando o Unbound ==<br />
Abaixo a configuração que usaremos nos servidores atentando para o detalhe do '''num-threads''', esse deve ter o valor igual ao número de CPUs do servidor.<br />
<br />
Também os IPs utilizados em '''outgoing-interface''' que serão diferentes em cada servidor, esses serão os IPs usados para '''recursividade'''. Consulte o manual do Unbound para obter mais informações sobre cada parâmetro listado na configuração.<br />
<br />
O tuning no Unbound pode ser alterado conforme abaixo:<br />
num-threads = nº CPUs<br />
so-reuseport = yes<br />
*-slabs = potência de 2 próximo ao num-threads<br />
msg-cache-size = 1G (quantidade de memória pra usar de cache)<br />
rrset-cache-size = 2 * msg-cache-size<br />
outgoing-range = 8192<br />
num-queries-per-thread = 4096<br />
so-rcvbuf e so-sndbuf = 4m ou 8m para servidores com muita requisição<br />
Agora vamos criar nosso arquivo de configuração base em '''/etc/unbound/unbound.conf.d/local.conf''':<br />
server:<br />
verbosity: 1<br />
statistics-interval: 0<br />
statistics-cumulative: no<br />
extended-statistics: yes<br />
num-threads: 6<br />
serve-expired: yes<br />
interface: 127.0.0.1<br />
interface: 10.10.10.10<br />
interface: 10.10.9.9<br />
interface: 172.16.0.6<br />
interface: ::1<br />
interface-automatic: no<br />
outgoing-interface: 198.18.1.10<br />
outgoing-interface: 2001:db8::faca:198:18:1:10<br />
outgoing-range: 8192<br />
outgoing-num-tcp: 1024<br />
incoming-num-tcp: 2048<br />
so-rcvbuf: 4m<br />
so-sndbuf: 4m<br />
so-reuseport: yes<br />
edns-buffer-size: 1232<br />
msg-cache-size: 1G<br />
msg-cache-slabs: 4<br />
num-queries-per-thread: 4096<br />
rrset-cache-size: 2G<br />
rrset-cache-slabs: 4<br />
infra-cache-slabs: 4<br />
do-ip4: yes<br />
do-ip6: yes<br />
do-udp: yes<br />
do-tcp: yes<br />
chroot: ""<br />
username: "unbound"<br />
directory: "/etc/unbound"<br />
logfile: "/var/log/unbound/unbound.log"<br />
use-syslog: no<br />
log-time-ascii: yes<br />
log-queries: no<br />
pidfile: "/var/run/unbound.pid"<br />
root-hints: "/etc/unbound/named.cache"<br />
hide-identity: yes<br />
hide-version: yes<br />
unwanted-reply-threshold: 10000000<br />
prefetch: yes<br />
prefetch-key: yes<br />
rrset-roundrobin: yes<br />
minimal-responses: yes<br />
module-config: "respip validator iterator"<br />
val-clean-additional: yes<br />
val-log-level: 1<br />
key-cache-slabs: 4<br />
deny-any: yes<br />
access-control: 198.18.0.0/22 allow<br />
access-control: 2001:db8::/32 allow<br />
<br />
rpz:<br />
name: rpz.block.host.local.zone<br />
zonefile: /etc/unbound/rpz.block.hosts.zone<br />
rpz-action-override: nxdomain<br />
<br />
python:<br />
<br />
auth-zone:<br />
name: "."<br />
master: "b.root-servers.net"<br />
master: "c.root-servers.net"<br />
master: "d.root-servers.net"<br />
master: "f.root-servers.net"<br />
master: "g.root-servers.net"<br />
master: "k.root-servers.net"<br />
master: "lax.xfr.dns.icann.org"<br />
master: "iad.xfr.dns.icann.org"<br />
fallback-enabled: yes<br />
for-downstream: no<br />
for-upstream: yes<br />
zonefile: "/var/lib/unbound/root.zone"<br />
No parâmetro '''interface''' colocamos os IPs que serão usados para consulta dos clientes como o '''10.10.10.10''' e o '''10.10.9.9'''. Ali repare que coloquei também o IP privado '''172.16.0.6''', isso porque cada servidor terá o seu IP privado e este deve ser usado pelo seu sistema de monitoramento para checar cada servidor. No '''outgoing-interface''' teremos os IPs, tanto '''IPv4''' quanto '''IPv6''', para que seja feita a recursividade na Internet utilizando eles. Não tem '''IPv6''' ainda na sua rede? Dê uma olhada nesse artigo. Outro parâmetro importante é o '''access-control''' e é através dele que liberamos os prefixos IP para consultarem no nosso DNS Recursivo. No exemplo estou liberando todo o prefixo '''198.18.0.0/22''' e o prefixo '''2001:db8::/32'''. Além da ACL no Unbound, recomendo que crie um filtro de pacotes com iptables ou nftables protegendo seu sistema e liberando as portas '''53/UDP''', '''53/TCP''' e '''443/TCP''' apenas para seus clientes. Falarei sobre a '''443/TCP''' mais para frente nessa mesma documentação.<br />
<br />
Agora criaremos o arquivo '''RPZ''' ('''Response Policy Zones'''). Esse arquivo contém os sites que serão bloqueados via '''<abbr>DNS</abbr> Recursivo'''. São aqueles sites que às vezes você recebe um Ofício da Justiça solicitando o bloqueio deles. Não entrarei no mérito da efetividade desses bloqueios, porque muitos de vocês sabem que tecnicamente, existem formas de se fazer um bypass através desses bloqueios. Contudo vamos deixar nosso ambiente preparado para esses bloqueios e por isso crie o arquivo '''/etc/unbound/rpz.block.hosts.zone''' com esse conteúdo de exemplo:<br />
$TTL 2h<br />
@ IN SOA localhost. root.localhost. (2 6h 1h 1w 2h)<br />
IN NS localhost.<br />
; RPZ manual block hosts<br />
*.josedascoves.com CNAME .<br />
josedascoves.com CNAME .<br />
No exemplo acima estamos bloqueando qualquer consulta de DNS para '''josedascoves.com''' ou qualquer coisa '''.josedascoves.com'''.<br />
<br />
Para testar podemos fazer assim do próprio servidor:<br />
# host josedascoves.com ::1<br />
Using domain server:<br />
Name: ::1<br />
Address: ::1#53<br />
Aliases:<br />
<br />
Host josedascoves.com not found: 3(NXDOMAIN)<br />
Se a resposta for '''NXDOMAIN''' então está funcionando o bloqueio. Para incluir novos bloqueios basta adicionar os domínios, um abaixo do outro, conforme o exemplo que coloquei no arquivo RPZ.<br />
<br />
== Acertando o resolv.conf ==<br />
Vamos modificar nosso /etc/resolv.conf para utilizar DNS externo. Sim você deve estar se perguntando em qual situação isso seria utilizado. Primeiro entenda que o Unbound não irá utilizar o DNS externo para fazer as consultas na Internet e sim, qualquer teste que você faça do servidor precisará apontar para o Unbound usando os IPs '''127.0.0.1''' ou '''::1'''. Faremos isso pela seguinte situação: imagine que o daemon unbound morreu mas você ainda continua com conectividade na Internet. Você conseguiria acessar qualquer local na Internet através do IP mas não através do hostname porque não conseguiria resolver nomes, seu unbound estaria fora do ar. Imagine ainda que você gostaria que seu servidor te avisasse do problema via Telegram ou e-mail. Por isso estamos utilizando um DNS externo no '''/etc/resolv.conf''', apenas para essas situações. Se você não quiser utilizar desse recurso, pode usar o '''127.0.0.1''' e '''::1''' no lugar.<br />
nameserver 8.8.8.8<br />
nameserver 8.8.4.4<br />
nameserver 2001:4860:4860::8888<br />
<br />
== Script de teste de recursividade ==<br />
Estamos montando uma '''Rede de DNS Recursivo Anycast''', então é muito importante que você monitore essa rede para saber se algum node morreu e iniciar o troubleshooting, resolver o problema e levantar o sistema novamente. Tudo isso é importante mas o cliente não deve ficar esperando até você resolver o problema, seu sistema precisa ser inteligente o suficiente para se remover da Rede quando tiver um problema e se inserir novamente, quando o problema estiver sido solucionado. Se você montar uma Rede de DNS e um dos nodes apresentar algum problema, todos os clientes atendidos por aquele node migrarão automaticamente e transparentemente para outro '''DNS Recursivo Anycast''' mais próximo. Isso se chama '''disponibilidade'''.<br />
<br />
O script '''/root/scripts/checa_dns.sh''' abaixo tem a função de fazer os testes de recursividade e checar se o daemon do unbound continua rodando. Se algo acontecer, ele para o anúncio do '''10.10.10.10''' e '''10.10.9.9''' e retorna eles quando tudo estiver resolvido.<br />
# mkdir /root/scripts<br />
<br />
#!/usr/bin/env bash<br />
#Script para teste de recursividade v2.2<br />
# Por Marcelo Gondim<br />
# Em 24-12-2022<br />
#<br />
# checa_dns.sh is free software; you can redistribute it and/or modify<br />
# it under the terms of the GNU General Public License as published by<br />
# the Free Software Foundation; either version 2 of the License, or<br />
# (at your option) any later version.<br />
#<br />
# This program is distributed in the hope that it will be useful,<br />
# but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
# GNU General Public License for more details.<br />
#<br />
# You should have received a copy of the GNU General Public License<br />
# along with this program; if not, write to the Free Software<br />
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
#-----------------------------------------------------------------------<br />
#Informe um domínio por linha:<br />
dominios_testar=(<br />
www.google.com<br />
www.terra.com.br<br />
www.uol.com.br<br />
www.globo.com<br />
www.facebook.com<br />
www.youtube.com<br />
www.twitch.com<br />
www.discord.com<br />
www.debian.org<br />
www.redhat.com<br />
)<br />
corte_taxa_falha=100 #Porcentagem de falha para executar uma ação<br />
#-----------------------------------------------------------------------<br />
remove_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" != "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'no network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME morreu!" | /usr/local/sbin/telegram-notify --error --text -<br />
fi<br />
}<br />
<br />
adiciona_ospf() {<br />
habilitado="`vtysh -c 'show run' | grep \"10.10.10.10\"`"<br />
if [ "$habilitado" == "" ]; then<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.10.10/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
vtysh -c 'conf t' -c 'router ospf' -c 'network 10.10.9.9/32 area 0.0.0.0' -c 'end' -c 'wr'<br />
#echo "Servidor $HOSTNAME retornou do inferno!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
}<br />
<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
#echo "Servidor $HOSTNAME morreu DNS mas tentando levantar!" | /usr/local/sbin/telegram-notify --error --text -<br />
systemctl restart unbound<br />
systemctl status unbound &> /dev/null;<br />
if [ $? -ne 0 ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
#echo "Servidor $HOSTNAME servico DNS voltou mas tinha morrido!" | /usr/local/sbin/telegram-notify --success --text -<br />
fi<br />
<br />
qt_falhas=0<br />
qt_total="${#dominios_testar[@]}"<br />
echo "total_dominios: $qt_total"<br />
for site in "${dominios_testar[@]}"<br />
do<br />
resolver="127.0.0.1"<br />
echo -e " - dominio $site - $resolver - \c"<br />
host $site $resolver &> /dev/null<br />
if [ $? -ne 0 ]; then<br />
((qt_falhas++))<br />
echo -e "[Falhou]"<br />
else<br />
echo -e "[OK]"<br />
fi<br />
done<br />
<br />
taxa_falha=$((qt_falhas*100/qt_total))<br />
echo "Falhas $qt_falhas/$qt_total ($taxa_falha%)"<br />
<br />
if [ "$taxa_falha" -ge "$corte_taxa_falha" ]; then<br />
remove_ospf<br />
exit<br />
fi<br />
adiciona_ospf<br />
Se rodarmos o script manualmente veremos isto:<br />
# /root/scripts/checa_dns.sh<br />
total_dominios: 10<br />
- dominio www.google.com - 127.0.0.1 - [OK]<br />
- dominio www.terra.com.br - 127.0.0.1 - [OK]<br />
- dominio www.uol.com.br - 127.0.0.1 - [OK]<br />
- dominio www.globo.com - 127.0.0.1 - [OK]<br />
- dominio www.facebook.com - 127.0.0.1 - [OK]<br />
- dominio www.youtube.com - 127.0.0.1 - [OK]<br />
- dominio www.twitch.com - 127.0.0.1 - [OK]<br />
- dominio www.discord.com - 127.0.0.1 - [OK]<br />
- dominio www.debian.org - 127.0.0.1 - [OK]<br />
- dominio www.redhat.com - 127.0.0.1 - [OK]<br />
Falhas 0/10 (0%)<br />
Se acontecer 100% de falhas o script irá remover os anúncios do OSPF. Se o daemon do unbound morrer, ele tentará reiniciá-lo. Se tudo normalizar o script irá retornar os anúncios para o OSPF. Deixei comentado no script as partes que enviariam uma notificação para o Telegram. Existem diversas documentações sobre isso na Internet, eu mesmo tenho uma. Assim que eu publicar aqui, atualizo essa documentação e sinta-se à vontade de modificar como desejar.<br />
# chmod 700 /root/scripts/checa_dns.sh<br />
Adicione a linha abaixo em seu '''/etc/crontab''':<br />
*/1 * * * * root /root/scripts/checa_dns.sh<br />
<br />
== Habilitando o DoH (<abbr>DNS</abbr> over HTTPS) - opcional ==<br />
Para habilitar o '''DoH''' no Unbound é bem simples mas só é possível com a versão '''1.17.1''' que vem no '''bullseye-backports''' e que virá na próxima versão do '''Debian 12 (Bookworm)'''. O recurso do '''DoH''' vem para trazer mais segurança e privacidade para o usuário. É um recurso muito pouco utilizado ainda mas que seu cliente pode vir a pedir algum dia.<br />
<br />
Você precisará gerar certificados SSL legítimos e para isso você poderá usar o '''Let's Encrypt''' só que de uma forma não tão convencional.<br />
<br />
Na sequência vamos instalar o Let's Encrypt para gerarmos nosso certificado SSL:<br />
# apt install letsencrypt<br />
Escolha um '''hostname''' para ser usado no nosso '''DoH''' e aponte ele no seu DNS Autoritativo para seus IPs 10.10.10.10 e 10.10.9.9. Aqui vamos usar o seguinte como exemplo: '''doh.brasilpeeringforum.org'''. Para gerarmos nosso certificado iremos usar o tipo '''DNS-01''', ele não necessita que tenhamos um servidor web rodando no servidor e nem tão pouco levanta um serviço na porta 80 para checar o hostname. Ele utiliza o DNS como validador e vai te solicitar que crie um registro '''CNAME''' no seu '''DNS Autoritativo''' para provar que você tem o controle sobre aquele hostname. Antes disso vamos instalar um programa em Python para podermos automatizar nossa renovação de certificado no futuro. Esse programa se encontra '''[https://github.com/joohoi/acme-dns-certbot-joohoi/raw/master/acme-dns-auth.py aqui]''' mas vou deixá-lo abaixo já modificado o interpretador.<br />
<br />
Crie o arquivo '''/etc/letsencrypt/acme-dns-auth.py''' com o conteúdo abaixo:<br />
#!/usr/bin/env python3<br />
import json<br />
import os<br />
import requests<br />
import sys<br />
<br />
### EDIT THESE: Configuration values ###<br />
<br />
# URL to acme-dns instance<br />
ACMEDNS_URL = "<nowiki>https://auth.acme-dns.io</nowiki>"<br />
# Path for acme-dns credential storage<br />
STORAGE_PATH = "/etc/letsencrypt/acmedns.json"<br />
# Whitelist for address ranges to allow the updates from<br />
# Example: ALLOW_FROM = ["192.168.10.0/24", "::1/128"]<br />
ALLOW_FROM = []<br />
# Force re-registration. Overwrites the already existing acme-dns accounts.<br />
FORCE_REGISTER = False<br />
<br />
### DO NOT EDIT BELOW THIS POINT ###<br />
### HERE BE DRAGONS ###<br />
<br />
DOMAIN = os.environ["CERTBOT_DOMAIN"]<br />
if DOMAIN.startswith("*."):<br />
DOMAIN = DOMAIN[2:]<br />
VALIDATION_DOMAIN = "_acme-challenge."+DOMAIN<br />
VALIDATION_TOKEN = os.environ["CERTBOT_VALIDATION"]<br />
<br />
<br />
class AcmeDnsClient(object):<br />
"""<br />
Handles the communication with ACME-DNS API<br />
"""<br />
<br />
def __init__(self, acmedns_url):<br />
self.acmedns_url = acmedns_url<br />
<br />
def register_account(self, allowfrom):<br />
"""Registers a new ACME-DNS account"""<br />
<br />
if allowfrom:<br />
# Include whitelisted networks to the registration call<br />
reg_data = {"allowfrom": allowfrom}<br />
res = requests.post(self.acmedns_url+"/register",<br />
data=json.dumps(reg_data))<br />
else:<br />
res = requests.post(self.acmedns_url+"/register")<br />
if res.status_code == 201:<br />
# The request was successful<br />
return res.json()<br />
else:<br />
# Encountered an error<br />
msg = ("Encountered an error while trying to register a new acme-dns "<br />
"account. HTTP status {}, Response body: {}")<br />
print(msg.format(res.status_code, res.text))<br />
sys.exit(1)<br />
<br />
def update_txt_record(self, account, txt):<br />
"""Updates the TXT challenge record to ACME-DNS subdomain."""<br />
update = {"subdomain": account['subdomain'], "txt": txt}<br />
headers = {"X-Api-User": account['username'],<br />
"X-Api-Key": account['password'],<br />
"Content-Type": "application/json"}<br />
res = requests.post(self.acmedns_url+"/update",<br />
headers=headers,<br />
data=json.dumps(update))<br />
if res.status_code == 200:<br />
# Successful update<br />
return<br />
else:<br />
msg = ("Encountered an error while trying to update TXT record in "<br />
"acme-dns. \n"<br />
"------- Request headers:\n{}\n"<br />
"------- Request body:\n{}\n"<br />
"------- Response HTTP status: {}\n"<br />
"------- Response body: {}")<br />
s_headers = json.dumps(headers, indent=2, sort_keys=True)<br />
s_update = json.dumps(update, indent=2, sort_keys=True)<br />
s_body = json.dumps(res.json(), indent=2, sort_keys=True)<br />
print(msg.format(s_headers, s_update, res.status_code, s_body))<br />
sys.exit(1)<br />
<br />
class Storage(object):<br />
def __init__(self, storagepath):<br />
self.storagepath = storagepath<br />
self._data = self.load()<br />
<br />
def load(self):<br />
"""Reads the storage content from the disk to a dict structure"""<br />
data = dict()<br />
filedata = ""<br />
try:<br />
with open(self.storagepath, 'r') as fh:<br />
filedata = fh.read()<br />
except IOError as e:<br />
if os.path.isfile(self.storagepath):<br />
# Only error out if file exists, but cannot be read<br />
print("ERROR: Storage file exists but cannot be read")<br />
sys.exit(1)<br />
try:<br />
data = json.loads(filedata)<br />
except ValueError:<br />
if len(filedata) > 0:<br />
# Storage file is corrupted<br />
print("ERROR: Storage JSON is corrupted")<br />
sys.exit(1)<br />
return data<br />
<br />
def save(self):<br />
"""Saves the storage content to disk"""<br />
serialized = json.dumps(self._data)<br />
try:<br />
with os.fdopen(os.open(self.storagepath,<br />
os.O_WRONLY | os.O_CREAT, 0o600), 'w') as fh:<br />
fh.truncate()<br />
fh.write(serialized)<br />
except IOError as e:<br />
print("ERROR: Could not write storage file.")<br />
sys.exit(1)<br />
<br />
def put(self, key, value):<br />
"""Puts the configuration value to storage and sanitize it"""<br />
# If wildcard domain, remove the wildcard part as this will use the<br />
# same validation record name as the base domain<br />
if key.startswith("*."):<br />
key = key[2:]<br />
self._data[key] = value<br />
<br />
def fetch(self, key):<br />
"""Gets configuration value from storage"""<br />
try:<br />
return self._data[key]<br />
except KeyError:<br />
return None<br />
<br />
if __name__ == "__main__":<br />
# Init<br />
client = AcmeDnsClient(ACMEDNS_URL)<br />
storage = Storage(STORAGE_PATH)<br />
<br />
# Check if an account already exists in storage<br />
account = storage.fetch(DOMAIN)<br />
if FORCE_REGISTER or not account:<br />
# Create and save the new account<br />
account = client.register_account(ALLOW_FROM)<br />
storage.put(DOMAIN, account)<br />
storage.save()<br />
<br />
# Display the notification for the user to update the main zone<br />
msg = "Please add the following CNAME record to your main DNS zone:\n{}"<br />
cname = "{} CNAME {}.".format(VALIDATION_DOMAIN, account["fulldomain"])<br />
print(msg.format(cname))<br />
<br />
# Update the TXT record in acme-dns instance<br />
client.update_txt_record(account, VALIDATION_TOKEN)<br />
<br />
# chmod +x /etc/letsencrypt/acme-dns-auth.py<br />
Usaremos a seguinte instrução para criar nosso certificado:<br />
# certbot certonly --manual --manual-auth-hook /etc/letsencrypt/acme-dns-auth.py --preferred-challenges dns --debug-challenges -d doh.brasilpeeringforum.org<br />
Saving debug log to /var/log/letsencrypt/letsencrypt.log<br />
Plugins selected: Authenticator manual, Installer None<br />
Cert is due for renewal, auto-renewing...<br />
Renewing an existing certificate for doh.brasilpeeringforum.org<br />
Performing the following challenges:<br />
dns-01 challenge for doh.brasilpeeringforum.org<br />
Running manual-auth-hook command: /etc/letsencrypt/acme-dns-auth.py<br />
Output from manual-auth-hook command acme-dns-auth.py:<br />
Please add the following CNAME record to your main DNS zone:<br />
_acme-challenge.doh.brasilpeeringforum.org CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
<br />
Waiting for verification...<br />
<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Challenges loaded. Press continue to submit to CA. Pass "-v" for more info about<br />
challenges.<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
Press Enter to Continue<br />
Nesse momento você cria o registro '''CNAME''' no seu DNS Autoritativo conforme ele solicitou: '''_acme-challenge.doh.brasilpeeringforum.org IN CNAME b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.''' e somente depois de criado e checado no DNS, você pressiona o '''Enter''' para continuar. Você pode checar dessa forma:<br />
# host -t cname _acme-challenge.doh.brasilpeeringforum.org<br />
_acme-challenge.doh.brasilpeeringforum.org is an alias for b555d682-7b50-45d9-a92f-3c3d187dd4e7.auth.acme-dns.io.<br />
Para que nosso certificado seja automaticamente renovado colocaremos no '''/etc/crontab''' a seguinte linha abaixo:<br />
00 00 1 * * root /usr/bin/certbot -q renew<br />
Acima temos a instrução para renovação automática do certificado. Repare que você vai precisar também copiar esse certificado para seus outros servidores, escolha um servidor para manter o certificado sempre atualizado e crie um script que faça a mesma cópia remotamente para os outros servidores. O '''scp''' e o '''rsync''' são seus aliados nisso.<br />
<br />
=== Configurando o Unbound ===<br />
Em nosso '''/etc/unbound/unbound.conf.d/local.conf''', adicionaremos no bloco "'''server:'''" o seguinte:<br />
interface: 10.10.10.10@443<br />
interface: 10.10.9.9@443<br />
tls-service-key: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/privkey.pem"<br />
tls-service-pem: "/etc/letsencrypt/live/doh.brasilpeeringforum.org/fullchain.pem"<br />
Para usar o recurso do '''DoH''' você precisará habilitar o recurso no seu navegador e informar a URL. Vou colocar o exemplo do '''Google Chrome''': Digite '''chrome://settings/security?search=dns''' no seu Chrome e ative '''Usar DNS seguro''', selecione '''Personalizado''' e adicione nossa URL:<br />
[[Arquivo:Doh bpf2.png|nenhum|commoldura]]<br />
<br />
== Finalizando ==<br />
Aqui finalizamos nosso projeto para uma Rede de DNS(s) Recursivos Anycast com Hyperlocal. Esse projeto é escalável, seguro, resiliente e você entregará muito mais qualidade de Internet para o seu cliente. Pare de entregar o '''8.8.8.8''' para os seus clientes, você está contribuindo para uma Internet mais lenta, sem a qualidade que o seu cliente merece. Investi meu tempo, que é muito pouco, para deixar esse documento para a comunidade, para você melhorar o seu ISP, para dar um UP! nele, então vamos começar 2023 com o pé direito. O que acha?<br />
<br />
Como prova de conceito, uma imagem abaixo onde temos uma Rede em produção de DNS(s) Recursivos Anycast e apontando exatamente o momento em que houve alguma situação que fez com que as queries de DNS, convergissem de um node para outro, de forma transparente e automática para o cliente. Podemos notar também que ao ser resolvido o problema, o tráfego retornou para o seu node correto:<br />
[[Arquivo:Convergencia.png|nenhum|commoldura]]<br />
<br />
== KINDNS (Stands for Knowledge-Sharing and Instantiating Norms for DNS and Naming Security) ==<br />
Achou que havia terminado? Agora que você tem a capacidade de montar uma '''Rede de DNS Recursivo''' com todas essas features acima, com todas as ferramentas que foram comentadas, o que acha de certificar o que fez?<br />
<br />
Assim como o [https://www.manrs.org/ MANRS] veio para certificar nosso sistema de roteamento na Internet, agora temos o [https://kindns.org/ KINDNS] para certificar que nossos sistemas de DNS estão bem feitos e dentro dos padrões de segurança. Existem '''7 ações''' que podem ser certificadas para nossos DNS Recursivos e estão aqui em https://kindns.org/shared-private-resolvers/. Com essa nossa documentação, se bem aplicada, você pode se candidatar ao KINDNS e ter seu ASN listado aqui https://kindns.org/participants/<br />
<br />
Obter e manter o '''MANRS''' e '''KINDNS''' demonstra seu compromisso com as Boas Práticas, contribui para termos uma '''Internet''' mais segura e te abre portas para novos negócios que possam exigir essas conformidades.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]<br />
__FORCARTDC__</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3558Identificando e neutralizando uma Botnet2023-07-11T04:06:03Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:Botnet banner.png|nenhum|commoldura]]<br />
<br />
Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
[[Arquivo:Botnet7.png|semmoldura|536x536px]]<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Obs.: o exemplo acima está sendo feito com egrep para enxergarmos os pacotes encapsulados mas se quisesse ver os pacotes PPPoE por exemplo: '''tcpdump -i enp2s0f0 -n "pppoes and host 100.64.224.6"''' <br />
<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criando uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não é tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3557Identificando e neutralizando uma Botnet2023-07-09T03:10:57Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:Botnet banner.png|nenhum|commoldura]]<br />
<br />
Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
[[Arquivo:Botnet7.png|semmoldura|536x536px]]<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Obs.: o exemplo acima está sendo feito com egrep para enxergarmos os pacotes encapsulados mas se quisesse ver os pacotes PPPoE por exemplo: '''tcpdump -i enp2s0f0 -n "pppoes and host 100.64.224.6"''' <br />
<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criando uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não é tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3556Identificando e neutralizando uma Botnet2023-07-08T18:14:09Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:Botnet banner.png|nenhum|commoldura]]<br />
<br />
Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
[[Arquivo:Botnet7.png|semmoldura|536x536px]]<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Obs.: o exemplo acima está sendo feito com egrep para enxergarmos os pacotes encapsulados mas se quisesse ver os pacotes PPPoE por exemplo: '''tcpdump -i enp2s0f0 -n "pppoes and host 100.64.224.6"''' <br />
<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não é tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet7.png&diff=3555Arquivo:Botnet7.png2023-07-08T18:13:48Z<p>Gondim: </p>
<hr />
<div>botnet7</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Usu%C3%A1rio:Gondim&diff=3554Usuário:Gondim2023-07-08T17:57:09Z<p>Gondim: </p>
<hr />
<div>'''Marcelo Gondim da Cunha'''<br />
[[Arquivo:Gondim paisagem.jpg|esquerda|commoldura]]<br />
<br />
'''Contribuições e trabalhos:'''<br />
<br />
* Administração de Sistemas Unix-Like desde 1996.<br />
* Consultor na Conectiva S/A - Unidade Rio em 2000.<br />
* Autor do projeto TuxFrw - https://tuxfrw.linuxinfo.com.br<br />
* Administração de sistemas BSD pela FreeBSD Brasil em 2010.<br />
* Direção do AS53135 - Nettel Telecomunicações entre 2003 e 2021 atingindo a marca de 41.000 assinantes. Gerando qualidade na entrega de serviços e implantando boas práticas como: <br />
<br />
a) IPv6.<br />
<br />
b) MANRS.<br />
<br />
c) RPKI.<br />
<br />
d) CPEs com firmware baseada na BCOP <nowiki>https://www.m3aawg.org/sites/default/files/lac-bcop-1-m3aawg-v1-portuguese-final.pdf</nowiki><br />
<br />
'''Palestras ministradas:'''<br />
<br />
* Semana da Informática UERJ 2002 - TuxFrw.<br />
* CONISLI 2003 - TuxFrw.<br />
* CONISLI 2004 - Fazendo compras com Gentoo Linux - Palestra sobre a distribuição Linux e suas ferramentas fantásticas.<br />
* CONISLI 2004 - OpenVPN - Palestra sobre como criar VPNs seguras com OpenVPN e diferenças entre ele e o IPSec.<br />
* CONISLI 2005 - SPoP (Security Point of Presence) com OpenVPN - Como utilizar túneis encriptados do OpenVPN para acessar a Internet de forma segura, onde quer que esteja.<br />
* SECOMP 2005 UNIFEI - TuxFrw.<br />
<br />
* Debconf19 2019 - [https://debconf19.debconf.org/talks/4-debian-na-vida-de-uma-operadora-de-telecom/ Debian na vida de uma operadora de Telecom], [https://www.youtube.com/watch?v=vQSTslUZy8k&list=PLYUtdmpYPTTJDtwgD8AtxzFJ9t_URhFMK&index=35 vídeo] e [https://salsa.debian.org/debconf-team/public/share/debconf19/raw/master/slides/4-debian-na-vida-de-uma-operadora-de-telecom.pdf?inline=false pdf].<br />
<br />
* [https://www.youtube.com/watch?v=5uOFtkplDts FiqueEmCasaUseDebian - CGNAT com NFTables] e [https://www.youtube.com/watch?v=Wz2IAg6MMlU SysAdmin apps].<br />
<br />
'''Artigos desenvolvidos para a comunidade do Brasil Peering Fórum:'''<br />
<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_na_pratica CGNAT na pratica].<br />
* [https://wiki.brasilpeeringforum.org/w/Acesso_via_IPv6_Link-Local Acesso via IPv6 Link-Local].<br />
* [https://wiki.brasilpeeringforum.org/w/MANRS MANRS].<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_Bulk_Port_Allocation_com_DPDK CGNAT Bulk Port Allocation com DPDK].<br />
* [https://wiki.brasilpeeringforum.org/w/Servidor_de_Logs Servidor de Logs].<br />
* [[DNS Recursivo Anycast Hyperlocal|DNS Anycast com Hyperlocal]].<br />
* [[Recomendações sobre Mitigação DDoS]]<br />
* [[Portas de Amplificação DDoS e Botnets]]<br />
* [[Static Loop - um erro que pode matar seu ISP/ITP]]<br />
* [[Identificando e neutralizando uma Botnet]]<br />
<br />
'''Tutorial:'''<br />
<br />
* [https://bit.ly/2saumHK Segurança de roteamento: MANRS (Mutually Agreed Norms for Routing Security) - Tutoriais NIC.br em 09/12/2019].<br />
<br />
'''[https://www.manrs.org/about/advisory-group/members/ MANRS Advisory Group Member (2020-2021)]'''.<br />
<br />
[https://www.youtube.com/watch?v=oahQkGx8urY '''Live sobre MANRS com Leonardo Furtado'''].<br />
<br />
Criação da Wiki ISPUP! em 24/12/2022.<br />
<br />
Semana de Capacitação 6 do NIC.br 28/04/2023 - '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT'''. Material [https://semanacap.bcp.nic.br/6-online/ aqui] e vídeo aula [https://www.youtube.com/watch?v=1q7J3NkQVSc aqui].<br />
<br />
'''Contatos:'''<br />
<br />
Telegram: '''@Marcelo_Gondim'''<br />
<br />
WhatsApp: +55 (22) 98827-9258<br />
<br />
E-mail: '''gondim at ispup.com.br'''<br />
<br />
Linkedin: https://www.linkedin.com/in/marcelo-gondim-sysadmin/<br />
<br />
Meu Github: https://github.com/gondimcodes<br />
<br />
== Mini-CV ==<br />
'''Marcelo Gondim''' começou sua carreira como desenvolvedor de software em COBOL e Clipper entre 1992 e 1995. Em 1996 foi responsável por desenvolver um sistema concorrente com o RENPAC da Embratel para acesso ao SISCOMEX e implantou a Internet para fins comerciais na empresa DATABRAS. Trabalhou como consultor e instrutor de Linux na Conectiva S/A em 2000. Em 2003 se tornou consultor de diversos Provedores de Internet na Região dos Lagos - RJ e onde acabou se tornando CTO da Nettel Telecomunicações (AS53135) com 42.000 assinantes. Implantou IPv6 iniciando em 2013 e se tornou participante do MANRS com diversas contribuições com artigos e palestras. Atualmente é Especialista em Redes, cuida do SOC (Security Operations Center) da Brasil TecPar AS262907, onde desenvolve as boas práticas, tratamentos de incidentes relacionados ao ASN e desenvolve as estratégias de Mitigação DDoS da empresa. Também desenvolveu uma Rede de DNS Recursivo Anycast espalhada pelo RS e MT/MS, também certificada KINDNS.</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3553Identificando e neutralizando uma Botnet2023-07-08T17:54:12Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:Botnet banner.png|nenhum|commoldura]]<br />
<br />
Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Obs.: o exemplo acima está sendo feito com egrep para enxergarmos os pacotes encapsulados mas se quisesse ver os pacotes PPPoE por exemplo: '''tcpdump -i enp2s0f0 -n "pppoes and host 100.64.224.6"''' <br />
<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não é tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3552Identificando e neutralizando uma Botnet2023-07-08T17:41:32Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:Botnet banner.png|nenhum|commoldura]]<br />
<br />
Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Obs.: o exemplo acima está sendo feito com egrep para enxergarmos os pacotes encapsulados mas se quisesse ver os pacotes PPPoE por exemplo: '''tcpdump -i enp2s0f0 -n "pppoes and host 100.64.224.6"''' <br />
<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3551Identificando e neutralizando uma Botnet2023-07-08T17:17:34Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:Botnet banner.png|nenhum|commoldura]]<br />
<br />
Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet_banner.png&diff=3550Arquivo:Botnet banner.png2023-07-08T17:16:38Z<p>Gondim: </p>
<hr />
<div>botnet</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3549Identificando e neutralizando uma Botnet2023-07-08T17:15:02Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3548Identificando e neutralizando uma Botnet2023-07-08T17:10:24Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos configurados com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3547Identificando e neutralizando uma Botnet2023-07-08T17:07:23Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
<br />
[[Arquivo:Botnet3.jpg|semmoldura|404x404px]]<br />
<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3546Identificando e neutralizando uma Botnet2023-07-08T17:06:54Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
[[Arquivo:Botnet3.jpg|nenhum|commoldura|400x400px]]<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3545Identificando e neutralizando uma Botnet2023-07-08T17:06:23Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
[[Arquivo:Botnet3.jpg|nenhum|commoldura|414x414px]]<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo. Podemos observar os pacotes '''SYN''' da tentativa de comunicação com os servidores do Botnet.<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]<br />
<br />
== Conclusão ==<br />
Precisamos manter nossa Infraestrutura de Redes sempre segura e principalmente os ativos na ponta do cliente como é o caso das CPEs. Escolham equipamentos corretamente, onde os fornecedores tenham preocupação com segurança, que suportem corretamente o IPv6. Equipamentos com usuário e senha padrão de fábrica são alvos fáceis para formação de Botnets. Procurem equipamentos que sigam a [https://www.lacnog.net/docs/lac-bcop-1 '''BCOP de CPEs'''] Trocar equipamentos de core, pode ser mais simples que trocar milhares de CPEs infectadas nos clientes. Pensem sempre nisso.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3544Identificando e neutralizando uma Botnet2023-07-08T16:53:25Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
[[Arquivo:Botnet3.jpg|nenhum|commoldura|414x414px]]<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | egrep "\.35342:"<br />
Então pegamos todos os servidores C&C e adicionamos em '''blackhole''' na borda, cortando a comunicação dos dispositivos infectados. De tempo em tempo o dispositivo tentava se comunicar com algum C&C conforme abaixo:<br />
[[Arquivo:Botnet5.jpg|nenhum|commoldura]]<br />
<br />
== Panorama após os bloqueios da Botnet ==<br />
Depois de todos os bloqueios realizados não foram mais registrados ataques saintes para a Internet. Esse tipo de ação não limpa o dispositivo infectado mas lhe dá tempo para fazer essa limpeza posteriormente, sem que os clientes sejam penalizados no processo.<br />
[[Arquivo:Botnet6.png|nenhum|commoldura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet6.png&diff=3543Arquivo:Botnet6.png2023-07-08T16:52:48Z<p>Gondim: </p>
<hr />
<div>botnet6</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet5.jpg&diff=3542Arquivo:Botnet5.jpg2023-07-08T16:45:21Z<p>Gondim: </p>
<hr />
<div>botnet5</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3541Identificando e neutralizando uma Botnet2023-07-08T16:26:00Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
[[Arquivo:Botnet3.jpg|nenhum|commoldura|414x414px]]<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre>[[Arquivo:Botnet4.png|semmoldura|1289x1289px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3540Identificando e neutralizando uma Botnet2023-07-08T16:24:52Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
[[Arquivo:Botnet3.jpg|nenhum|commoldura|414x414px]]<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre><br />
[[Arquivo:Botnet4.png|nenhum|commoldura|1318x1318px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3539Identificando e neutralizando uma Botnet2023-07-08T16:24:14Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==<br />
Para analisarmos os pacotes do dispositivo infectado vamos utilizar o programa '''tcpdump'''. Caso não conheça e não tenha familiaridade com ele, recomendo a leitura deste livro '''Análise de Tráfego em Redes TCP/IP do autor João Eriberto Mota Filho'''.<br />
[[Arquivo:Botnet3.jpg|nenhum|commoldura|414x414px]]<br />
Após eleger um dispositivo infectado, pegamos o IP dele para monitorarmos suas atividades em busca dos ataques quando ocorrem. Irei colocar aqui exemplos reais de uma recente Botnet descoberta e neutralizada. Colocamos nosso '''sniffer (tcpdump)''' monitorando conforme o exemplo abaixo:<br />
# tcpdump -i enp2s0f0 -n | egrep "100\.64\.224\.6\." | tee botnet.txt<br />
Nesse exemplo acima estamos monitorando a interface de 10GbE '''enp2s0f0''' , observando os pacotes com IP do dispositivo '''100.64.224.6''' e armazenando no arquivo '''botnet.txt'''. Quando perceber o pico sainte de ataque no flow da ferramenta de análise que comentei, por exemplo o Wanguard, teremos registrado os pacotes que precisamos e então podemos encerrar o comando acima e analisar nosso '''botnet.txt'''. Faremos o comando abaixo e observem meu nosso exemplo:<pre><br />
# egrep "100\.64\.224\.6\." botnet.txt |less<br />
</pre><br />
[[Arquivo:Botnet4.png|nenhum|commoldura|1322x1322px]]<br />
Dentro da janela '''2''' podemos ver o início do disparo do ataque para a vítima '''IP 103.216.154.99''', diversos pacotes '''ACK''' para a porta de destino '''88/TCP'''. Na janela 1 podemos perceber o início da comunicação entre o dispositivo infectado e o servidor C&C no '''IP 77.105.138.202 porta 35342/TCP'''. Logo após a comunicação entre eles, se inicia o ataque para a vítima.<br />
<br />
Podemos fazer uma busca via whois nesse IP e coletar dados para posterior contato com o ASN responsável pelo IP e também consultar o [https://www.peeringdb.com/ '''PeeringDB''']. Para cortar a comunicação com o C&C, a forma mais efetiva e sem gerar custos de CPU é criado uma '''blackhole''' na sua borda para esse IP do servidor C&C. Infelizmente esse trabalho não tão rápido pois em uma Botnet pode haver mais de um servidor C&C e por isso precisamos continuar monitorando e bloqueando os IPs até que os ataques parem.<br />
<br />
Depois de uma análise melhor percebemos que todos os servidores C&C estavam escutando na porta '''35342/TCP''' e então começamos a monitorar o IP e porta assim:</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet4.png&diff=3538Arquivo:Botnet4.png2023-07-08T16:07:55Z<p>Gondim: </p>
<hr />
<div>botnet4</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet3.jpg&diff=3537Arquivo:Botnet3.jpg2023-07-08T15:47:42Z<p>Gondim: </p>
<hr />
<div>botnet3</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3536Identificando e neutralizando uma Botnet2023-07-08T15:39:07Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento BNG, por exemplo em um Mikrotik usando o torch, mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento a 100% e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.<br />
<br />
Monte um equipamento para esse tipo de análise e já deixe pré configurado em seu ambiente:<br />
{| class="wikitable"<br />
|+<br />
!CPU<br />
!Sistema<br />
!Memória<br />
!Disco<br />
!Interface de Rede<br />
|-<br />
|Quad Core<br />
|Debian GNU/Linux<br />
|8 a 16Gb<br />
|SSD 120Gb<br />
|Intel X520-SR2 (2 portas de 10GbE)<br />
|}<br />
<br />
== Analisando os pacotes ==</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3535Identificando e neutralizando uma Botnet2023-07-08T15:27:41Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Coleta inicial de informações ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.<br />
[[Arquivo:Botnet2.png|nenhum|commoldura]]<br />
<br />
== Próximo passo montar ambiente de análise da Botnet ==<br />
Agora que temos alguns candidatos infectados, precisamos montar um ambiente lab para que possamos monitorar e registrar os ataques. Observar o comportamento deles e quem aciona eles. Para isso vamos precisar de um GNU/Linux, que neste artigo será um Debian mas poderá ser qualquer outra distribuição que tenha um '''tcpdump''' para fazermos a análise dos pacotes, precisaremos de uma interface apropriada que consiga receber o tráfego da interface por onde estão vindo os ataques. Exemplo:<br />
<br />
Vamos supor que os ataques estejam vindo de uma interface onde tem um '''BNG/B-RAS PPPoE''' e o tráfego seja de uns 4Gbps, nesse caso colocaríamos uma '''interface de 10Gbps''' nesse GNU/Linux, ligamos na '''switch''' e fazemos um '''espelhamento (mirror)''' da porta do BNG/B-RAS para a porta onde temos nosso GNU/Linux. Dessa forma todo o tráfego passante na porta do BNG/B-RAS poderá ser analisado no GNU/Linux. Alguns teriam a ideia de fazer a análise diretamente no equipamento, por exemplo em um Mikrotik BNG mas não aconselho pois esse tipo de análise pode elevar o consumo de CPU do seu equipamento e isso não é bom. Então o jeito mais seguro e sem mexer muito no seu ambiente de produção, é esse que estou propondo.</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet2.png&diff=3534Arquivo:Botnet2.png2023-07-08T15:08:04Z<p>Gondim: </p>
<hr />
<div>botnet2</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3533Identificando e neutralizando uma Botnet2023-07-08T15:07:23Z<p>Gondim: </p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.<br />
<br />
== Panorama de uma Botnet em andamento e atacando uma vítima ==<br />
Para vermos os estragos que uma Botnet pode causar abaixo um gráfico dos momentos de ataques que saíam da Operação com destino à vítimas:<br />
[[Arquivo:Botnet1.png|nenhum|commoldura]]<br />
Na imagem acima podemos observar diversos picos de mais de 4Gbps com destino a Internet. Agora vamos imaginar o que esses picos estariam causando em suas portas PON das suas OLTs e quantos clientes poderiam estar sendo afetados. Quanto maior a infecção da Botnet maior será o poder de ataque desses cibercriminosos. Percebam que só é possível identificar visualmente essa situação, se vocês tiverem ferramentas de monitoramento como um Zabbix por exemplo. Monitoramento é vital e fundamental para identificarmos algo impactante em nossa Operação.<br />
<br />
== Ações importantes ==<br />
Para tratar esse incidente, precisaremos identificar pelo menos 1 dispositivo infectado e observar o tráfego desse dispositivo para entendermos como é a comunicação entre os bots e seus C&C. Apenas pelo monitoramento acima não teremos essa resposta, nesse caso entra como um dos fatores de busca a ferramenta de análise de anomalias, como por exemplo: '''Fastnetmon''', '''Wanguard''', '''Kentik'''. A ideia é analisar os flows coletados na hora da saída do ataque, observar as anomalias de uploads que estourem thresholds de upload e ainda podemos analisar casos envolvendo pacotes UDP, que causam mais estragos. Desse jeito podemos eleger alguns possíveis candidatos infectados para que possamos monitorar de perto.</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Botnet1.png&diff=3532Arquivo:Botnet1.png2023-07-08T14:41:50Z<p>Gondim: </p>
<hr />
<div>Botnet1</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Identificando_e_neutralizando_uma_Botnet&diff=3531Identificando e neutralizando uma Botnet2023-07-08T14:37:32Z<p>Gondim: Criou página com 'Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebido...'</p>
<hr />
<div>Atualmente a maior preocupação de todo ISP (Internet Service Provider) e ITP (Internet Transit Provider) são os ataques '''DDoS (Distributed Denial of Service)''' recebidos em suas Redes e a indisponibilidade que eles podem causar derrubando sua infraestrutura e seus clientes. Criamos mecanismos de defesa como: sistemas de detecção de anomalias como por exemplo o Wanguard, anunciamos os prefixos atacados para nossas nuvens de mitigação e assim fazemos a limpeza do tráfego. Mas e quando sua rede também está fazendo parte dos ataques de DDoS e atacando outros ASNs na Internet? Poucas pessoas se preocupam com o que saem de suas Redes e isso pode estar impactando sua operação e causando insatisfação em seus clientes. As '''Portas de Amplificação''' '''abertas''' e '''Botnets''' são as principais causas para esses problemas. Como tratar Portas de Amplificação você pode ler nesse meu artigo [[Portas de Amplificação DDoS e Botnets|'''Portas de Amplificação DDoS e Botnets''']] mas aqui vamos falar de algo um pouco mais trabalhoso, que seria como identificar e neutralizar uma '''Botnet''' que se instalou na sua Rede.<br />
<br />
== Entendendo uma Botnet ==<br />
Primeiro temos que entender como elas funcionam: são diversos, milhares de dispositivos vulneráveis que são invadidos e infectados com programas (bots) que se conectam em servidores chamados '''C&C (command and control)'''. O dispositivo pode ser qualquer um infectado como CPEs, ONUs roteadas, computadores, enfim qualquer coisa que tenha um IP e possa acessar a Internet. Esses dispositivos infectados uma vez conectados nesses C&C podem receber e executar diversas tarefas para o cibercriminoso como por exemplo: envio de spam, roubo de informações e orquestrar ataques sincronizados em uma determinada vítima. Esses ataques saem com uma certa volumetria da sua rede e isso pode saturar diversos pontos da sua infraestrutura causando indisponibilidade para seus clientes. Seria um DDoS ao contrário, da sua Operação para a Internet.</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=CGNAT_na_pratica&diff=3523CGNAT na pratica2023-07-06T05:40:35Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:773px-Cgnat nic br.jpg|nenhum|miniaturadaimagem|773x773px]]<br />
<br />
==Objetivo==<br />
Com o esgotamento do IPv4 mundialmente, precisamos tomar algumas providências para que a Internet não pare. As que vejo de imediatas são: '''IPv6''' e '''CGNAT (Carrier Grade NAT)'''. O '''IPv6''' é a real solução para os problemas de esgotamento e o CGNAT seria a "gambiarra" necessária para continuar com o IPv4 até que a Internet esteja 100% em IPv6. Nesse artigo será explicado como montar uma caixa CGNAT Determinística usando '''GNU/Linux''' e '''Mikrotik RouterOS'''. Esse artigo foi baseado no treinamento da '''Semana de Capacitação''' do '''NIC.br''' e que pode ser encontrado com o título '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT''' [https://semanacap.bcp.nic.br/6-online/ '''aqui'''] como palestra e material de apoio e o vídeo do treinamento no '''Youtube''' '''[https://www.youtube.com/watch?v=1q7J3NkQVSc aqui]'''.<br />
<br />
== Diagrama ==<br />
[[Arquivo:776px-Cgnat diagrama2.png|esquerda|miniaturadaimagem|776x776px]]<br />
No '''BNG''' é configurado uma '''PBR (Policy Based Routing)''' onde apenas IPs do bloco '''100.64.0.0/22''' serão roteados diretamente para a '''caixa CGNAT'''. Qualquer IPv4 público ou IPv6, serão roteados diretamente para a '''Borda'''. Isso evita processamento e tráfego desnecessário na '''caixa CGNAT'''. <br />
<br />
No diagrama ao lado a linha '''amarela''' simboliza o tráfego do bloco '''100.64.0.0/22''' indo para o '''CGNAT'''. A linha '''vermelha''' seria o tráfego já traduzido para um IP da rede '''198.18.0.0/27''' e encaminhado para a '''Borda'''. A linha '''verde''' é o tráfego mais limpo, sem "gambiarras" e o real objetivo que devemos seguir para uma '''Internet''' melhor usando IPv6.<br />
<br />
A '''Borda''' é um equipamento onde podemos inserir algumas regras de filtros de pacotes stateless para filtrar alguns pacotes indesejados como por exemplo: determinados spoofings e BOGONs. Também onde serão feitas ACLs para filtros BGP. '''Ação 1''' e '''2''' do '''MANRS'''.<br />
<br />
O '''Cliente''' nesse diagrama aparece conectado com o '''IPv4''' de CGNAT '''100.64.0.2''' e '''IPv6 2001:0db8:f18:0:a941:6164:1a79:c0f3'''. Todo o acesso IPv4 desse cliente e nesse exemplo, para a Internet, sairá com o IP '''198.18.0.0''' usando as portas entre '''5056''' e '''7071''', conforme mostraremos no script gerador de regras de CGNAT.<br />
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br />
<br />
<br />
<br />
== CGNAT no GNU/Linux ==<br />
<br />
=== Hardware e Sistema que utilizaremos no GNU/Linux ===<br />
* 2x Intel® Xeon® Silver 4215R Processor (3.20 GHz, 11M Cache, 8 núcleos/16 threads). Ambiente NUMA (non-uniform memory access).<br />
* 32Gb de ram.<br />
* 2x SSD 240 Gb RAID1.<br />
* 2x Interfaces de rede Intel XL710-QDA2 (2 portas de 40 Gbps).<br />
* GNU/Linux Debian 11 (Bullseye).<br />
<br />
Vamos configurar um LACP com as duas portas de cada interface, para que possamos ter um backup, caso algum módulo apresente algum problema. Seu ambiente de produção pode ser diferente e por isso precisamos ter alguns cuidados na hora de montarmos o conjunto de hardware e não obtermos surpresas.<br />
<br />
1º Verifique algumas especificações da interface de rede que será usada. Por exemplo a '''Intel XL710-QDA2''':<br />
<br />
* 2 portas de 40 Gbps.<br />
* PCIe 3.0 x8 (8.0 GT/s).<br />
<br />
Com essa informação seu equipamento não poderá possuir slots PCIe inferiores a esta especificação, caso contrário terá problemas de desempenho.<br />
<br />
Você também precisa estar atento para as limitações de barramento por '''versão''' x '''lane''' ('''x1'''):<br />
<br />
* PCIe 1.0/1.1 - 2.5 GT/s - (8b/10b encoding) - 2 Gbps.<br />
* PCIe 2.0/2.1 - 5.0 GT/s - (8b/10b encoding) - 4 Gbps.<br />
* PCIe 3.0/3.1 - 8.0 GT/s - (128b/130b encoding) - ~7,88 Gbps.<br />
* PCIe 4.0 - 16 GT/s - (128b/130b encoding) - ~15,76 Gbps.<br />
<br />
=== Calculando a capacidade ===<br />
Se observarmos a '''XL710-QDA2''' é '''PCIe 3.0 x8 (8 lanes)''' ou seja o barramento irá suportar:<br />
<br />
* '''8.0 GT/s * (128b/130b encoding) * 8 lanes = 63,01 Gbps'''<br />
<br />
O objetivo do '''LACP''' nesse caso, '''não seria alcançar os''' '''80 Gbps de capacidade''' em cada interface, mesmo porque cada barramento das interfaces é '''limitado em 63,01 Gbps''', mas manteremos um '''backup dos 40 Gbps'''. <br />
<br />
Nessa configuração teríamos teoricamente '''63,01 Gbps de entrada e 63,01 Gbps de saída'''. Mas para esse cenário precisaremos fazer uma coisa chamada '''CPU Affinity'''. Nesse caso colocaríamos um processador dedicado para cada interface de rede. É um cenário mais complexo do que com 1 processador apenas, inclusive necessitamos de olhar o datasheet da motherboard e identificar quais slots PCIe são diretamente controlados por qual CPU. Se temos a '''CPU0''' e '''CPU1''', uma interface precisará ficar no slot controlado pela '''CPU0''' e a outra interface no slot controlado pela '''CPU1''' e '''observar a quantidade de lanes no slot para ver se suporta a mesma quantidade de lanes da interface de rede'''.<br />
<br />
Falando um pouco sobre PPS (Packet Per Second) para calcular por exemplo 1 Gbps de tráfego na ethernet, a quantidade de PPS que o sistema precisaria suportar encaminhar teríamos: 1.000.000.000/8/1518 = 82.345 packets per second.<br />
<br />
Existe um comando no GNU/Linux para você saber se o seu equipamento com processadores físicos, conseguirá trabalhar com o '''CPU Affinity''':<br />
# cat /sys/class/net/<interface>/device/numa_node<br />
Se o resultado do comando acima for '''-1''' então esse equipamento não trabalhará com o '''CPU Affinity'''. Isso porque cada interface precisa estar sendo gerenciada por um node específico. Se são 2 processadores então o resultado deveria ser '''0 de CPU0''' ou '''1 de CPU1'''.<br />
<br />
A seguir veremos um exemplo de datasheet da '''motherboard S2600WF''':<br />
[[Arquivo:903px-S2600wf.png|nenhum|miniaturadaimagem|903x903px]]<br />
Se observarmos o datasheet acima veremos que temos o '''PCIe Riser #1''', '''Riser #2''' e '''Riser #3'''. Cada Riser possui slots PCIe que são gerenciados por determinada CPU. Se colocássemos as duas interfaces de rede nos '''slots do Riser #2''' e '''Riser #3''', estaríamos pendurando tudo apenas no '''processador 2'''. Isso foi apenas para mostrar a complexidade de quando usamos um equipamento '''NUMA''' e estamos somente escolhendo o hardware adequado. Ainda não chegamos na configuração do '''CPU Affinity'''.<br />
<br />
Para sabermos quais cores estão relacionados para uma determinada CPU, utilizamos os comandos abaixo:<br />
# cat /sys/devices/system/node/node0/cpulist<br />
0-7<br />
<br />
# cat /sys/devices/system/node/node1/cpulist<br />
8-15<br />
No exemplo acima a '''CPU0''' '''tem os cores de 0 a 7''' e a '''CPU1, os cores de 8 a 15''', ou seja, é um equipamento com '''16 cores'''.<br />
<br />
=== Tuning antes do CPU Affinity ===<br />
Também é importante, para aumento de performance, que seja '''desabilitado na BIOS o HT (Hyper Threading)'''.<br />
<br />
Antes de configurarmos algumas coisas no nosso ambiente, precisaremos instalar uma ferramenta importante para o nosso tuning; vamos instalar o pacote '''ethtool'''. Ele servirá para fazermos alguns ajustes nas nossas interfaces de rede. Alguns fabricantes podem não permitir certas alterações mas com as interfaces da Intel sempre obtive os resultados esperados. <br />
# apt install ethtool<br />
No nosso exemplo acima vimos que o equipamento possui '''16 cores''' sendo que '''8 cores por CPU'''. Então, para esse caso, faremos um ajuste nas interfaces para ficarem preparadas para receberem '''8 cores em cada através das IRQs'''. Usamos o parâmetro '''-l''' do '''ethtool''' para listar o '''Pre-set maximums combined''' da interface e o parâmetro '''-L''' para alterar esse valor. Façamos então a alteração:<br />
# ethtool -L enp5s0f0 combined 8<br />
# ethtool -L enp5s0f1 combined 8<br />
# ethtool -L enp6s0f0 combined 8<br />
# ethtool -L enp6s0f1 combined 8<br />
Com os comandos acima deixamos preparadas as interfaces para aceitarem '''8 cores em cada uma através das IRQs'''. <br />
<br />
Não podemos usar o programa '''irqbalance''' para o '''CPU Affinity''', pois este faz migração de contextos entre os cores e isso é ruim. Como no nosso exemplo estamos usando uma interface Intel, utilizaremos um script da própria Intel para realizar o '''CPU Affinity''' de forma mais fácil. Esse script se chama '''set_irq_affinity''' e vem acompanhado com os fontes do driver da interface. Ex.: '''[https://www.intel.com/content/www/us/en/download/18026/intel-network-adapter-driver-for-pcie-40-gigabit-ethernet-network-connections-under-linux.html Intel Network Adapter]'''<br />
<br />
=== Código do script '''set_irq_affinity''' ===<br />
#!/bin/bash<br />
# SPDX-License-Identifier: BSD-3-Clause<br />
# Copyright (c) 2015 - 2019, Intel Corporation<br />
#<br />
# Affinitize interrupts to cores<br />
#<br />
# typical usage is (as root):<br />
# set_irq_affinity -x local eth1 <eth2> <eth3><br />
# set_irq_affinity -s eth1<br />
#<br />
# to get help:<br />
# set_irq_affinity<br />
<br />
usage()<br />
{<br />
echo<br />
echo "Usage: option -s <interface> to show current settings only"<br />
echo "Usage: $0 [-x|-X] [all|local|remote [<node>]|one <core>|custom|<cores>] <interface> ..."<br />
echo " Options: "<br />
echo " -s Shows current affinity settings"<br />
echo " -x Configure XPS as well as smp_affinity"<br />
echo " -X Disable XPS but set smp_affinity"<br />
echo " [all] is the default value"<br />
echo " [remote [<node>]] can be followed by a specific node number"<br />
echo " Examples:"<br />
echo " $0 -s eth1 # Show settings on eth1"<br />
<br />
echo " $0 all eth1 eth2 # eth1 and eth2 to all cores"<br />
echo " $0 one 2 eth1 # eth1 to core 2 only"<br />
echo " $0 local eth1 # eth1 to local cores only"<br />
echo " $0 remote eth1 # eth1 to remote cores only"<br />
echo " $0 custom eth1 # prompt for eth1 interface"<br />
echo " $0 0-7,16-23 eth0 # eth1 to cores 0-7 and 16-23"<br />
echo<br />
exit 1<br />
}<br />
<br />
usageX()<br />
{<br />
echo "options -x and -X cannot both be specified, pick one"<br />
exit 1<br />
}<br />
<br />
if [ "$1" == "-x" ]; then<br />
XPS_ENA=1<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-s" ]; then<br />
SHOW=1<br />
echo Show affinity settings<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-X" ]; then<br />
if [ -n "$XPS_ENA" ]; then<br />
usageX<br />
fi<br />
XPS_DIS=2<br />
shift<br />
fi<br />
<br />
if [ "$1" == -x ]; then<br />
usageX<br />
fi<br />
<br />
if [ -n "$XPS_ENA" ] && [ -n "$XPS_DIS" ]; then<br />
usageX<br />
fi<br />
<br />
if [ -z "$XPS_ENA" ]; then<br />
XPS_ENA=$XPS_DIS<br />
fi<br />
<br />
SED=`which sed`<br />
if <nowiki>[[ ! -x $SED ]]</nowiki>; then<br />
echo " $0: ERROR: sed not found in path, this script requires sed"<br />
exit 1<br />
fi<br />
<br />
num='^[0-9]+$'<br />
<br />
# search helpers<br />
NOZEROCOMMA="s/^[0,]*//"<br />
# Vars<br />
AFF=$1<br />
shift<br />
<br />
case "$AFF" in<br />
remote) <nowiki>[[ $1 =~ $num ]]</nowiki> && rnode=$1 && shift ;;<br />
one) <nowiki>[[ $1 =~ $num ]]</nowiki> && cnt=$1 && shift ;;<br />
all) ;;<br />
local) ;;<br />
custom) ;;<br />
[0-9]*) ;;<br />
-h|--help) usage ;;<br />
"") usage ;;<br />
*) IFACES=$AFF && AFF=all ;; # Backwards compat mode<br />
esac<br />
<br />
# append the interfaces listed to the string with spaces<br />
while [ "$#" -ne "0" ] ; do<br />
IFACES+=" $1"<br />
shift<br />
done<br />
<br />
# for now the user must specify interfaces<br />
if [ -z "$IFACES" ]; then<br />
usage<br />
exit 2<br />
fi<br />
<br />
notfound()<br />
{<br />
echo $MYIFACE: not found<br />
exit 15<br />
}<br />
<br />
# check the interfaces exist<br />
for MYIFACE in $IFACES; do<br />
grep -q $MYIFACE /proc/net/dev || notfound<br />
done<br />
<br />
# support functions<br />
<br />
build_mask()<br />
{<br />
VEC=$core<br />
if [ $VEC -ge 32 ]<br />
then<br />
MASK_FILL=""<br />
MASK_ZERO="00000000"<br />
let "IDX = $VEC / 32"<br />
for ((i=1; i<=$IDX;i++))<br />
do<br />
MASK_FILL="${MASK_FILL},${MASK_ZERO}"<br />
done<br />
<br />
let "VEC -= 32 * $IDX"<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X%s" $MASK_TMP $MASK_FILL)<br />
else<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X" $MASK_TMP)<br />
fi<br />
}<br />
<br />
show_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
HINT=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/affinity_hint`<br />
printf "ACTUAL %s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf "HINT %s %d %s <- /proc/irq/$IRQ/affinity_hint\n" $IFACE $core $HINT<br />
IRQ_CHECK=`grep '[-,]' /proc/irq/$IRQ/smp_affinity_list`<br />
if [ ! -z $IRQ_CHECK ]; then<br />
printf " WARNING -- SMP_AFFINITY is assigned to multiple cores $IRQ_CHECK\n"<br />
fi<br />
if [ "$SMP_I" != "$HINT" ]; then<br />
printf " WARNING -- SMP_AFFINITY VALUE does not match AFFINITY_HINT \n"<br />
fi<br />
printf "NODE %s %d %s <- /proc/irq/$IRQ/node\n" $IFACE $core `cat /proc/irq/$IRQ/node`<br />
printf "LIST %s %d [%s] <- /proc/irq/$IRQ/smp_affinity_list\n" $IFACE $core `cat /proc/irq/$IRQ/smp_affinity_list`<br />
printf "XPS %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` ]; then<br />
echo "WARNING: xps rxqs not supported on $IFACE"<br />
else<br />
printf "XPSRXQs %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_rxqs\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` $IFACE $((n-1))<br />
fi<br />
printf "TX_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/tx_maxrate\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/tx_maxrate` $IFACE $((n-1))<br />
printf "BQLIMIT %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit` $IFACE $((n-1))<br />
printf "BQL_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_max\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_max` $IFACE $((n-1))<br />
printf "BQL_MIN %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_min\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_min` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` ]; then<br />
echo "WARNING: aRFS is not supported on $IFACE"<br />
else<br />
printf "RPSFCNT %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_flow_cnt\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` $IFACE $((n-1))<br />
fi<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` ]; then<br />
echo "WARNING: rps_cpus is not available on $IFACE"<br />
else<br />
printf "RPSCPU %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` $IFACE $((n-1))<br />
fi<br />
echo<br />
}<br />
<br />
set_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
printf "%s" $MASK > /proc/irq/$IRQ/smp_affinity<br />
printf "%s %d %s -> /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $MASK<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
if [ "$SMP_I" != "$MASK" ]; then<br />
printf " ACTUAL\t%s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf " WARNING -- SMP_AFFINITY setting failed\n"<br />
fi<br />
case "$XPS_ENA" in<br />
1)<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
2)<br />
MASK=0<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
*)<br />
esac<br />
}<br />
<br />
# Allow usage of , or -<br />
#<br />
parse_range () {<br />
RANGE=${@//,/ }<br />
RANGE=${RANGE//-/..}<br />
LIST=""<br />
for r in $RANGE; do<br />
# eval lets us use vars in {#..#} range<br />
<nowiki>[[ $r =~ '..' ]]</nowiki> && r="$(eval echo {$r})"<br />
LIST+=" $r"<br />
done<br />
echo $LIST<br />
}<br />
<br />
# Affinitize interrupts<br />
#<br />
doaff()<br />
{<br />
CORES=$(parse_range $CORES)<br />
ncores=$(echo $CORES | wc -w)<br />
n=1<br />
<br />
# this script only supports interrupt vectors in pairs,<br />
# modification would be required to support a single Tx or Rx queue<br />
# per interrupt vector<br />
<br />
queues="${IFACE}-.*TxRx"<br />
<br />
irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(for i in `ls -1 /sys/class/net/${IFACE}/device/msi_irqs | sort -n` ;do grep -w $i: /proc/interrupts | egrep -v 'fdir|async|misc|ctrl' | cut -f 1 -d :; done)<br />
[ -z "$irqs" ] && echo "Error: Could not find interrupts for $IFACE"<br />
<br />
if [ "$SHOW" == "1" ] ; then<br />
echo "TYPE IFACE CORE MASK -> FILE"<br />
echo "============================"<br />
else<br />
echo "IFACE CORE MASK -> FILE"<br />
echo "======================="<br />
fi<br />
<br />
for IRQ in $irqs; do<br />
[ "$n" -gt "$ncores" ] && n=1<br />
j=1<br />
# much faster than calling cut for each<br />
for i in $CORES; do<br />
[ $((j++)) -ge $n ] && break<br />
done<br />
core=$i<br />
if [ "$SHOW" == "1" ] ; then<br />
show_affinity<br />
else<br />
set_affinity<br />
fi<br />
((n++))<br />
done<br />
}<br />
<br />
# these next 2 lines would allow script to auto-determine interfaces<br />
#[ -z "$IFACES" ] && IFACES=$(ls /sys/class/net)<br />
#[ -z "$IFACES" ] && echo "Error: No interfaces up" && exit 1<br />
<br />
# echo IFACES is $IFACES<br />
<br />
CORES=$(</sys/devices/system/cpu/online)<br />
[ "$CORES" ] || CORES=$(grep ^proc /proc/cpuinfo | cut -f2 -d:)<br />
<br />
# Core list for each node from sysfs<br />
node_dir=/sys/devices/system/node<br />
for i in $(ls -d $node_dir/node*); do<br />
i=${i/*node/}<br />
corelist[$i]=$(<$node_dir/node${i}/cpulist)<br />
done<br />
<br />
for IFACE in $IFACES; do<br />
# echo $IFACE being modified<br />
<br />
dev_dir=/sys/class/net/$IFACE/device<br />
[ -e $dev_dir/numa_node ] && node=$(<$dev_dir/numa_node)<br />
[ "$node" ] && [ "$node" -gt 0 ] || node=0<br />
<br />
case "$AFF" in<br />
local)<br />
CORES=${corelist[$node]}<br />
;;<br />
remote)<br />
[ "$rnode" ] || { [ $node -eq 0 ] && rnode=1 || rnode=0; }<br />
CORES=${corelist[$rnode]}<br />
;;<br />
one)<br />
[ -n "$cnt" ] || cnt=0<br />
CORES=$cnt<br />
;;<br />
all)<br />
CORES=$CORES<br />
;;<br />
custom)<br />
echo -n "Input cores for $IFACE (ex. 0-7,15-23): "<br />
read CORES<br />
;;<br />
[0-9]*)<br />
CORES=$AFF<br />
;;<br />
*)<br />
usage<br />
exit 1<br />
;;<br />
esac<br />
<br />
# call the worker function<br />
doaff<br />
done<br />
<br />
# check for irqbalance running<br />
IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?`<br />
if [ "$IRQBALANCE_ON" == "0" ] ; then<br />
echo " WARNING: irqbalance is running and will"<br />
echo " likely override this script's affinitization."<br />
echo " Please stop the irqbalance service and/or execute"<br />
echo " 'killall irqbalance'"<br />
exit 2<br />
fi<br />
<br />
=== CPU Affinity ===<br />
Agora que preparamos as interfaces, façamos os apontamentos dos cores da seguinte forma. Vamos supor que colocamos o script em '''/root/scripts''':<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
<br />
=== Mais alguns tunings ===<br />
Vamos fazer mais alguns ajustes nas interfaces com o '''ethtool'''. Dessa vez vamos aumentar os '''Rings RX''' e '''TX'''. Mas antes vamos listar os valores que podemos usar:<br />
# ethtool -g enp5s0f0<br />
Ring parameters for enp5s0f0:<br />
Pre-set maximums:<br />
RX: 4096<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 4096<br />
Current hardware settings:<br />
RX: 512<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 512<br />
Acima vemos que o '''valor máximo é de 4096 tanto para TX''', '''quanto para RX''' mas está '''configurado para 512 em RX e TX'''. Façamos então:<br />
# ethtool -G enp5s0f0 rx 4096 tx 4096<br />
# ethtool -G enp5s0f1 rx 4096 tx 4096<br />
# ethtool -G enp6s0f0 rx 4096 tx 4096<br />
# ethtool -G enp6s0f1 rx 4096 tx 4096<br />
Vamos desabilitar as seguintes options das interfaces: '''TSO''', '''GRO''' e '''GSO'''. <br />
# ethtool -K enp5s0f0 tso off gro off gso off<br />
# ethtool -K enp5s0f1 tso off gro off gso off<br />
# ethtool -K enp6s0f0 tso off gro off gso off<br />
# ethtool -K enp6s0f1 tso off gro off gso off<br />
Aumentaremos o '''txqueuelen''' para '''10000''':<br />
# ip link set enp5s0f0 txqueuelen 10000<br />
# ip link set enp5s0f1 txqueuelen 10000<br />
# ip link set enp6s0f0 txqueuelen 10000<br />
# ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Salvando a configuração e criando o LACP ===<br />
Tudo que fizemos até o momento será perdido no próximo reboot do sistema, então faremos com que esses comandos sejam executados sempre que o sistema iniciar. Para isso vamos deixar o nosso arquivo '''/etc/network/interfaces''' configurado conforme nosso diagrama, usando '''LACP''' e executando nossos comandos anteriores.<br />
<br />
Antes precisaremos instalar o pacote '''ifenslave''' para que o bonding funcione:<br />
# apt install ifenslave<br />
# modprobe bonding<br />
# echo "bonding" >> /etc/modules<br />
Abaixo o nosso '''/etc/network/interfaces''' já com todas as configurações que fizemos anteriormente e seguindo nosso diagrama de exemplo:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto bond0<br />
iface bond0 inet static<br />
bond-slaves enp5s0f0 enp5s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 10.0.10.172/24<br />
gateway 10.0.10.1<br />
pre-up /usr/sbin/ethtool -L enp5s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp5s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
pre-up /usr/sbin/ethtool -G enp5s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp5s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp5s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp5s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp5s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp5s0f1 txqueuelen 10000<br />
<br />
auto bond1<br />
iface bond1 inet static<br />
bond-slaves enp6s0f0 enp6s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 192.168.0.1/24<br />
pre-up /usr/sbin/ethtool -L enp6s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp6s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
pre-up /usr/sbin/ethtool -G enp6s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp6s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp6s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp6s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp6s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Atualizando o Kernel ===<br />
Colocaremos o '''kernel do backports'''. Para isso deixe o seu '''/etc/apt/sources''' conforme abaixo e rode os comandos na sequência:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
<br />
# apt update<br />
# apt install -t bullseye-backports linux-image-amd64<br />
# reboot<br />
<br />
=== Protegendo contra static loop e preparando o ambiente do CGNAT ===<br />
O '''static loop''' é algo que, definitivamente, pode derrubar toda a sua operação se não for devidamente tratado e pode ser facilmente explorado por pessoas mal intencionadas. A causa do problema é uma rota estática para um prefixo IP (seja IPv4 ou IPv6), que aponta para um next-hop e nesse destino não existe nenhuma informação sobre o prefixo IP na tabela de rotas local, obrigando o pacote a retornar para o seu gateway default e ficando nesse loop até que '''expire o TTL (Time To Live) do pacote'''. Isso ocorre muito nos casos em que temos concentradores PPPoE (BNG) e caixas CGNAT como esta que estaremos fazendo. Em '''[[Recomendações sobre Mitigação DDoS]]''' temos outras dicas de segurança sobre o assunto '''DDoS'''.<br />
<br />
Crie um arquivo /etc/rc.local e dentro colocaremos algumas coisas como as blackholes para cada prefixo IPv4 público que usaremos no nosso servidor de exemplo e rotas de retorno para o nosso BNG:<br />
# > /etc/rc.local<br />
# chmod +x /etc/rc.local<br />
Dentro teremos:<br />
#!/bin/sh -e<br />
/usr/sbin/ip route add blackhole 198.18.0.0/27 metric 254<br />
/usr/sbin/ip route add 100.64.0.0/22 via 192.168.0.2<br />
No exemplo acima estamos colocando em '''blackhole''' o nosso prefixo IPv4 público deste tutorial que é o '''198.18.0.0/27''' e adicionando uma rota de retorno do prefixo '''100.64.0.0/22''' usado no nosso '''BNG''' para o '''next-hop 192.168.0.2'''.<br />
<br />
=== Redução dos tempos de timeouts e outros ajustes ===<br />
Os tempos padrões dos '''timeouts''' de '''tcp''' e '''udp''' são altos para o nosso sistema de CGNAT, ainda mais quando estamos '''diminuindo a quantidade de portas tcp/udp por assinante''' e com isso podemos rapidamente estourar esse limite, fazendo com que o sistema pare de funcionar. Abaixo estou colocando os valores que sempre usei e não percebi problemas, mas você pode ajustar conforme achar mais prudente. Adicionaremos as configurações abaixo também no nosso '''/etc/rc.local''': <br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_sent<br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_recv<br />
echo 86400 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_fin_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_last_ack<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_max_retrans<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_unacknowledged<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout<br />
echo 180 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_icmp_timeout<br />
echo 600 > /proc/sys/net/netfilter/nf_conntrack_generic_timeout<br />
Em '''/etc/sysctl.conf''' adicionaremos:<br />
net.core.default_qdisc=fq<br />
net.ipv4.tcp_congestion_control=bbr<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.ipv4.conf.all.forwarding=1<br />
net.netfilter.nf_conntrack_helper=1<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
As configurações acima melhoram o uso de memória, habilita o encaminhamento dos pacotes e aumenta a quantidade máxima de '''conntracks''' do sistema para '''4096000'''.<br />
<br />
'''Se o conntrack estourar, seu CGNAT terá problemas e causará indisponibilidades'''. Para consultar a quantidade de conntracks em uso:<br />
# cat /proc/sys/net/netfilter/nf_conntrack_count<br />
Para listar as '''conntracks''':<br />
# cat /proc/net/nf_conntrack<br />
<br />
=== Ajustando a data e horário do sistema ===<br />
Uma tarefa muito importante a ser feita nos servidores, é garantir que o horário e data estejam corretos e para isso usaremos o programa '''chrony'''. Eu prefiro usar sempre horário UTC nos servidores e fazer a conversão quando necessário:<br />
# apt install chrony<br />
Basta copiar e colar os comandos abaixo, para configurar o '''chrony''':<br />
# cat << EOF > /etc/chrony/chrony.conf<br />
confdir /etc/chrony/conf.d<br />
sourcedir /run/chrony-dhcp<br />
sourcedir /etc/chrony/sources.d<br />
keyfile /etc/chrony/chrony.keys<br />
driftfile /var/lib/chrony/chrony.drift<br />
ntsdumpdir /var/lib/chrony<br />
logdir /var/log/chrony<br />
maxupdateskew 100.0<br />
rtcsync<br />
makestep 1 3<br />
leapsectz right/UTC<br />
EOF<br />
<br />
# cat << EOF > /etc/chrony/sources.d/nic.sources<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
EOF<br />
Aqui reiniciamos o serviço e configuramos o '''timezone''':<br />
# systemctl restart chronyd.service<br />
# timedatectl set-timezone "UTC"<br />
<br />
=== Habilitando ALG (Application Layer Gateway) ===<br />
No arquivo '''/etc/modules''' adicionaremos os módulos que usaremos no nosso CGNAT, inclusive os ALGs'''.''' Sem eles alguns serviços, ainda muito utilizados, apresentarão problemas.<br />
<br />
Em '''/etc/modules''' adicionaremos mais os módulos abaixo:<br />
nf_conntrack<br />
nf_nat_pptp<br />
nf_nat_h323<br />
nf_nat_sip<br />
nf_nat_irc<br />
nf_nat_ftp<br />
nf_nat_tftp<br />
<br />
=== Preparando ambiente e gerador de regras de CGNAT ===<br />
Antes de começarmos nossas regras de CGNAT precisaremos de alguns pacotes:<br />
# apt install python3-pip nftables<br />
# pip install ipaddress<br />
Vamos precisar também de um gerador de regras de CGNAT para '''nftables'''. Porque criar as regras manualmente não é uma tarefa rápida e para isso usaremos um programa em python criado por '''José Beiriz''' e disponibilizado aqui: '''[https://github.com/Beiriz/GRCN GRCN]'''<br />
<br />
Caso não consigam baixar por algum motivo o '''GRCN''', aqui '''https://github.com/gondimcodes/GRCN''' também pode ser encontrado o script '''cgnat-nft.py.'''<br />
<br />
Nosso sistema de regras CGNAT será dividido em 2 partes:<br />
<br />
* O script base que colocaremos em '''/root/scripts''' chamado de '''frw-nft.sh'''. Esse script conterá as regras básicas do CGNAT e este incluirá a chamada para os outros arquivos de regras propriamente ditos do CGNAT. <br />
<br />
* Essa outra parte é composta pelos arquivos de regras de CGNAT, onde são feitas as traduções de IPs privados '''100.64.0.0/10 (Shared Address Space - RFC6598)''', para os '''IPs públicos'''. A seguir o '''frw-nft.sh''':<br />
<br />
Nosso script de CGNAT base '''/root/scripts/frw-nft.sh''':<br />
#!/usr/sbin/nft -f<br />
# limpa todas as regras da memoria<br />
flush ruleset<br />
<br />
# regras base para o CGNAT<br />
add table ip nat<br />
add chain ip nat POSTROUTING { type nat hook postrouting priority 100; policy accept; }<br />
<br />
add chain ip nat CGNATOUT<br />
<br />
# libera o proprio CGNAT para acessar a Internet - para atualizacoes por exemplo<br />
add rule ip nat POSTROUTING oifname "bond0" ip saddr 10.0.10.172 counter snat to 198.18.0.0<br />
<br />
# faz o jump para as regras de CGNAT<br />
add rule ip nat POSTROUTING oifname "bond0" counter jump CGNATOUT<br />
<br />
'''# carrega os arquivos de regras de CGNAT'''<br />
include "/root/scripts/cgnat-0-31.conf"<br />
A última linha do script acima, em '''negrito''', é o arquivo de regras CGNAT que iremos gerar e será chamado pelo script quando for executado.<br />
<br />
Após a criação do script, alteramos a permissão dele para ficar como executável e adicionamos ele em nosso '''/etc/rc.local''':<br />
# chmod 700 /root/scripts/frw-nft.sh<br />
# echo "/root/scripts/frw-nft.sh" >> /etc/rc.local<br />
<br />
=== Gerando nossas regras de CGNAT ===<br />
Colocaremos o script '''cgnat-nft.py''' em '''/root/scripts/'''. Como estamos trabalhando '''no modelo determinístico de 1/32''', basta pegarmos nosso bloco privado '''100.64.0.0/22 (1024 IPs)''' e nosso bloco público '''198.18.0.0/27 (32 IPs)''' e executarmos em linha de comando:<br />
# cd /root/scripts<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
Se digitar apenas '''./cgnat-nft.py''' será apresentado um help dos parâmetros mas é bem simples o seu uso. No comando acima '''temos o número 0 como índice'''. Muito cuidado com o índice, porque ele é muito importante para a performance e para cada novo arquivo gerado, esse índice precisará ser incrementado. O comando acima criará automaticamente o arquivo chamado '''cgnat-0-31.conf''', aquele mesmo visto no script base sendo carregado com o '''include'''. Onde esse '''0-31''' quer dizer que nesse arquivo '''os índices vão de 0 a 31'''. Se for gerar um novo arquivo com o comando acima, o próximo índice a ser usado seria o '''32'''. Por exemplo:<br />
# ./cgnat-nft.py '''32''' 198.18.0.32/27 100.64.4.0/22 1/32<br />
Esse comando acima criará novas regras no arquivo chamado '''cgnat-32-63.conf''', na sequência inclua esse novo arquivo dentro do '''/root/scripts/frw-nft.sh''' e '''execute o /root/scripts/frw-nft.sh novamente''' para carregar as novas regras. A seguir daremos uma olhada nas regras geradas nesses arquivos.<br />
<br />
=== Executando o gerador de regras ===<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
[[Arquivo:1022px-Grcn.png|nenhum|miniaturadaimagem|1022x1022px]]<br />
Após teclar '''ENTER''' será gerado o arquivo '''cgnat-0-31.conf''' com as regras conforme a tela abaixo de exemplo:<br />
[[Arquivo:1027px-Regras01.png|nenhum|miniaturadaimagem|1027x1027px]]<br />
Na tela abaixo se observarmos o '''retângulo vermelho''' veremos a regra que faz o '''NAT de tudo que não for TCP ou UDP''' e por fim a regra que faz o '''jump''' '''de tudo que for origem 100.64.0.0/27''' para o '''CGNATOUT_0''' onde esse '''0 é o índice'''.<br />
[[Arquivo:1029px-Regras02.png|nenhum|miniaturadaimagem|1029x1029px]]<br />
<br />
=== Explicando a função dos índices ===<br />
O sistema de avaliação de regras de filtros de pacotes e NAT no GNU/Linux é do tipo '''First Match Win''', o que significa que a pesquisa das regras se encerra quando o sistema encontra uma regra que dê match. O sistema fica muito mais otimizado e performático quando quebramos as regras e separamos em '''CHAINS''' e é aí que entram os '''índices'''. Porque as '''CHAINS''' não podem ter o mesmo nome, senão não haveria separação das regras. A seguir veremos por exemplo que quando houver um pacote relacionado com o prefixo de origem '''100.64.0.0/27''', este será encaminhado para a chain '''CGNATOUT_0''', que é onde estão as regras de CGNAT para esse bloco IP. Desse jeito a checagem para esse prefixo não percorre todas as regras de NAT contidas na memória.<br />
[[Arquivo:Regras03.png|nenhum|miniaturadaimagem|1034x1034px]]<br />
<br />
=== Novo script gerador de regras nftables com suporte a netmap ===<br />
Com a versão nova que virá do '''Debian''', a '''versão 12 (Bookworm)''', teremos também uma versão nova do '''nftables''' '''1.0.6''' e essa versão já suporta o equivalente ao '''netmap''' que temos no '''Mikrotik''' e com isso teremos menos regras na memória e provavelmente mais performance. O sistema novo conta também com o '''kernel 6.1.27''' que possui diversas '''melhorias na pilha tcp/ip'''. Para aqueles que já quiserem testar nesse novo ambiente, fiz uma modificação no script python mostrado anteriormente, para gerar regras nesse novo formato e um arquivo tabela com o relacionamento de portas e IPs para quebra de sigilo tecnológico. Aqui '''https://github.com/gondimcodes/GRCN''' o novo código e estarei solicitando ao '''José Beiriz''' para incorporá-lo no '''GRCN'''.<br />
Para gerar as regras é só executar da mesma maneira. Exemplo:<pre><br />
# ./cgnat-nft-netmap.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
</pre>Abaixo exemplos de como ficam as novas regras e na memória:<br />
[[Arquivo:Nftables netmap1.png|nenhum|commoldura|926x926px]]<br />
[[Arquivo:Nftables netmap2.png|nenhum|commoldura|929x929px]]<br />
Exemplo do arquivo '''tabela-0-31.txt''' que foi gerado:<br />
[[Arquivo:Nftables netmap3.png|nenhum|commoldura|963x963px]]<br />
<br />
=== Simulando um acesso do cliente e observando os resultados ===<br />
Para testar as regras, foi criado um ambiente virtual de laboratório usando um '''Proxmox''' e criando 3 VMs: '''CGNAT''', '''BNG''' e '''CLIENTE'''. Do router de testes capturei os pacotes para demonstrar como funciona o CGNAT. A seguir teremos o acesso por parte do cliente e a captura dos pacotes somente para uma '''POC (Proof of Concept)''', para demonstrar que o CGNAT está funcionando e alocando a porta, dentro do range de portas, corretamente para um determinado cliente.<br />
<br />
Abaixo temos um exemplo de captura bem simples de pacote mostrando que o '''IP 198.18.0.0''' com '''porta origem''' '''6767/TCP''' acessou o '''200.147.41.220 na porta 443/TCP''', um acesso para o site do '''UOL'''.<br />
[[Arquivo:Cgnat sniffer.png|nenhum|miniaturadaimagem|1039x1039px]]<br />
Se olharmos os dados marcados acima e procurarmos pelo IP '''198.18.0.0 e porta 6767''' no nosso arquivo de configuração do CGNAT, '''acharemos o IP 100.64.0.2''' '''que utiliza o range de portas entre 5056 e 7071'''. Abaixo o nosso arquivo de regras de CGNAT para comprovar o range de portas utilizados.<br />
[[Arquivo:Regras5.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Monitorando o tráfego em tempo real ===<br />
Monitorando o '''tráfego Mbps/PPS''' com a ferramenta '''bmon'''. Para instalar o software no Debian basta fazer: <br />
# apt install bmon<br />
Para monitorar as interfaces faríamos algo assim onde '''-b''' para '''bits/s''' e o '''-p''' para '''selecionar as interfaces que quer monitorar'''. Para monitorar nosso '''bond0''' e '''bond1''' o comando seria esse abaixo:<br />
# bmon -b -p bond0,bond1<br />
Abaixo uma tela de exemplo do '''bmon''' em execução:<br />
[[Arquivo:Bmon cgnat.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
== CGNAT no Mikrotik RouterOS ==<br />
Uma boa opção para caixa CGNAT com custo x benefício acessível seria uma '''CCR1036-8G-2S+''' onde se for configurada somente para fazer '''CGNAT''', com o '''mínimo de regras de filtro''' e '''Fasttrack habilitado''', já alcancei '''13 Gbps de tráfego ou 26 Gbps agregado''' fazendo um '''bonding com as 2 interfaces ópticas de 10Gbps'''.<br />
<br />
Essa imagem abaixo foi retirada do '''datasheet da CCR1036-8G-2S+''':<br />
[[Arquivo:Datasheet ccr1036.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Configurando o sistema ===<br />
Instale um '''Mikrotik RouterOS do zero''', procure utilizar a '''versão mais estável possível'''. Como não utilizei ainda em produção o RouterOS 7.x, '''sugiro utilizar a versão 6.48.6 Long-term''', que até o momento, é a versão considerada mais estável. O processo de configurar um '''CGNAT Determinístico no Mikrotik RouterOS''' será bem mais simples que no '''Debian GNU/Linux''' mas a capacidade alcançada com o '''GNU/Linux''' será bem superior ao visto aqui.<br />
<br />
=== Sobre Fasttrack ===<br />
O '''Fasttrack''' é um recurso muito importante que aumentará a performance da sua caixa CGNAT, '''acelerando o encaminhamento de pacotes''' e '''diminuindo o consumo de CPU'''. Neste momento não faremos isso. Quando chegarmos no processo de criação das regras de CGNAT, ele será habilitado e será mostrado quais as regras que fazem isso.<br />
<br />
=== Configurando o bonding ===<br />
Como usaremos as '''duas portas de 10GbE sfp+ da CCR''', utilizaremos vlans para separar a rede que se comunicará com a Internet, da rede com o BNG. A seguir veremos como deixar o nosso bonding. Na sequência configuramos nossas vlans de entrada e saída e em cima delas '''os IPs do diagrama''', como fizemos com o Debian. Vamos definir a '''vlan 101''' '''para a interface que fará a comunicação com a Internet''' e '''por onde será feito o CGNAT''' e a '''vlan 102 que fará a comunicação com o BNG'''.<br />
[[Arquivo:Cgnat mk1.png|nenhum|miniaturadaimagem|1043x1043px]]<br />
[[Arquivo:Cgnat mk2.png|nenhum|miniaturadaimagem|1237x1237px]]<br />
<br />
=== Configurando os IPs e rotas ===<br />
O objetivo deste artigo é ser bem simples para entendermos os conceitos e por isso estamos utilizando '''rotas estáticas''' e não estamos envolvendo outros protocolos como o '''OSPF'''. Nada impediria de utilizar a mesma técnica apresentada aqui em um cenário com '''OSPF''', por exemplo.<br />
<br />
A seguir veremos que na '''vlan-101-borda''' configuramos o '''IP 10.0.10.172/24''' e na '''vlan-102-bng''' configuramos o '''IP 192.168.0.1/24'''.<br />
<br />
Como rotas criamos uma '''default route''' apontando para o '''IP 10.0.10.1''', criamos uma rota para '''100.64.0.0/22''' com '''next-hop 192.168.0.2''' e para nos '''protegermos de static loop''' teremos nossas rotas de '''blackhole''' quando formos gerar as regras de CGNAT.<br />
<br />
Na imagem aparece como '''unreachable''' porque esse equipamento, que está sendo usado como lab, não está conectado em uma switch.<br />
[[Arquivo:Cgnat mk3.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
=== Recomendações de segurança ===<br />
<br />
* Utilize credenciais de acesso '''com senhas fortes''', não esqueça o login admin sem senha (padrão no Mikrotik RouterOS).<br />
<br />
* Desabilite todos os serviços que não for utilizar e os que ficarem abertos, especifique neles o acesso apenas da sua rede de gerência. Não deixe qualquer serviço aberto para a Internet.<br />
<br />
* Habilite o '''TCP SynCookies'''.<br />
<br />
Procure criar suas regras de filtros de pacotes sempre na '''Table Raw''', ela não agride tanto a performance do equipamento mas '''necessita de muita atenção''' porque ela pode afetar os acessos dos assinantes. Isso porque uma regra genérica demais será analisada tanto com destino a caixa, quanto destino ao cliente e o mesmo pode ocorrer no sentido inverso, do cliente para a Internet.<br />
[[Arquivo:Cgnat mk4.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Acertando data e hora ===<br />
Configure o '''NTP client da caixa''' e mantenha a data e horário sincronizados.<br />
[[Arquivo:Cgnat mk5.png|nenhum|miniaturadaimagem|1042x1042px]]<br />
<br />
=== Criando as regras de CGNAT ===<br />
Para simplificar nossa vida, '''Rudimar Remontti''' criou em seu blog, um sistema para gerar regras de '''CGNAT Determinístico''' de forma simples e performática, '''utilizando regras netmap da Mikrotik'''. Para tanto o link é este:<br />
<br />
'''https://cgnat.remontti.com.br/'''<br />
<br />
O sistema é bem completo, simples, irá gerar as '''regras de CGNAT''' e nossas '''blackholes''' para '''bloqueio de static loop'''. Também no final teremos uma '''tabela de associação''' que devemos guardar para fazer as quebras de sigilo solicitadas nos Ofícios Judiciais.<br />
<br />
Ao acessar o site e seguindo o nosso diagrama completaremos as informações conforme mostrado a seguir.<br />
[[Arquivo:Cgnat remontti1.png|nenhum|miniaturadaimagem|1047x1047px]]<br />
O site irá gerar automaticamente os comandos de onde faremos uma cópia e executaremos no nosso equipamento '''Mikrotik RouterOS'''.<br />
[[Arquivo:Cgnat remontti2.png|nenhum|miniaturadaimagem|1049x1049px]]<br />
No final da página é gerado uma tabela do mapeamento das portas, isso deve ser salvo como documento importante pois será usado para quebra de sigilo tecnológico.<br />
[[Arquivo:Cgnat remontti3.png|nenhum|miniaturadaimagem|1052x1052px]]<br />
O conceito é o mesmo, quebrar as regras em blocos menores para chegarmos no nosso '''First Match Win mais rápido''' e não termos que percorrer todas as regras em memória.<br />
[[Arquivo:Cgnat remontti4.png|nenhum|miniaturadaimagem|1053x1053px]]<br />
Abaixo como ficaram as regras que habilita o '''Fasttrack''' no nosso equipamento, aumentando em muito a performance de encaminhamento dos pacotes.<br />
[[Arquivo:Cgnat mk6.png|nenhum|miniaturadaimagem|1056x1056px]]<br />
<br />
== Conclusão ==<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=CGNAT_na_pratica&diff=3522CGNAT na pratica2023-07-06T05:39:28Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:773px-Cgnat nic br.jpg|nenhum|miniaturadaimagem|773x773px]]<br />
<br />
==Objetivo==<br />
Com o esgotamento do IPv4 mundialmente, precisamos tomar algumas providências para que a Internet não pare. As que vejo de imediatas são: '''IPv6''' e '''CGNAT (Carrier Grade NAT)'''. O '''IPv6''' é a real solução para os problemas de esgotamento e o CGNAT seria a "gambiarra" necessária para continuar com o IPv4 até que a Internet esteja 100% em IPv6. Nesse artigo será explicado como montar uma caixa CGNAT Determinística usando '''GNU/Linux''' e '''Mikrotik RouterOS'''. Esse artigo foi baseado no treinamento da '''Semana de Capacitação''' do '''NIC.br''' e que pode ser encontrado com o título '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT''' [https://semanacap.bcp.nic.br/6-online/ '''aqui'''] como palestra e material de apoio e o vídeo do treinamento no '''Youtube''' '''[https://www.youtube.com/watch?v=1q7J3NkQVSc aqui]'''.<br />
<br />
== Diagrama ==<br />
[[Arquivo:776px-Cgnat diagrama2.png|esquerda|miniaturadaimagem|776x776px]]<br />
No '''BNG''' é configurado uma '''PBR (Policy Based Routing)''' onde apenas IPs do bloco '''100.64.0.0/22''' serão roteados diretamente para a '''caixa CGNAT'''. Qualquer IPv4 público ou IPv6, serão roteados diretamente para a '''Borda'''. Isso evita processamento e tráfego desnecessário na '''caixa CGNAT'''. <br />
<br />
No diagrama ao lado a linha '''amarela''' simboliza o tráfego do bloco '''100.64.0.0/22''' indo para o '''CGNAT'''. A linha '''vermelha''' seria o tráfego já traduzido para um IP da rede '''198.18.0.0/27''' e encaminhado para a '''Borda'''. A linha '''verde''' é o tráfego mais limpo, sem "gambiarras" e o real objetivo que devemos seguir para uma '''Internet''' melhor usando IPv6.<br />
<br />
A '''Borda''' é um equipamento onde podemos inserir algumas regras de filtros de pacotes stateless para filtrar alguns pacotes indesejados como por exemplo: determinados spoofings e BOGONs. Também onde serão feitas ACLs para filtros BGP. '''Ação 1''' e '''2''' do '''MANRS'''.<br />
<br />
O '''Cliente''' nesse diagrama aparece conectado com o '''IPv4''' de CGNAT '''100.64.0.2''' e '''IPv6 2001:0db8:f18:0:a941:6164:1a79:c0f3'''. Todo o acesso IPv4 desse cliente e nesse exemplo, para a Internet, sairá com o IP '''198.18.0.0''' usando as portas entre '''5056''' e '''7071''', conforme mostraremos no script gerador de regras de CGNAT.<br />
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br />
<br />
<br />
<br />
== CGNAT no GNU/Linux ==<br />
<br />
=== Hardware e Sistema que utilizaremos no GNU/Linux ===<br />
* 2x Intel® Xeon® Silver 4215R Processor (3.20 GHz, 11M Cache, 8 núcleos/16 threads). Ambiente NUMA (non-uniform memory access).<br />
* 32Gb de ram.<br />
* 2x SSD 240 Gb RAID1.<br />
* 2x Interfaces de rede Intel XL710-QDA2 (2 portas de 40 Gbps).<br />
* GNU/Linux Debian 11 (Bullseye).<br />
<br />
Vamos configurar um LACP com as duas portas de cada interface, para que possamos ter um backup, caso algum módulo apresente algum problema. Seu ambiente de produção pode ser diferente e por isso precisamos ter alguns cuidados na hora de montarmos o conjunto de hardware e não obtermos surpresas.<br />
<br />
1º Verifique algumas especificações da interface de rede que será usada. Por exemplo a '''Intel XL710-QDA2''':<br />
<br />
* 2 portas de 40 Gbps.<br />
* PCIe 3.0 x8 (8.0 GT/s).<br />
<br />
Com essa informação seu equipamento não poderá possuir slots PCIe inferiores a esta especificação, caso contrário terá problemas de desempenho.<br />
<br />
Você também precisa estar atento para as limitações de barramento por '''versão''' x '''lane''' ('''x1'''):<br />
<br />
* PCIe 1.0/1.1 - 2.5 GT/s - (8b/10b encoding) - 2 Gbps.<br />
* PCIe 2.0/2.1 - 5.0 GT/s - (8b/10b encoding) - 4 Gbps.<br />
* PCIe 3.0/3.1 - 8.0 GT/s - (128b/130b encoding) - ~7,88 Gbps.<br />
* PCIe 4.0 - 16 GT/s - (128b/130b encoding) - ~15,76 Gbps.<br />
<br />
=== Calculando a capacidade ===<br />
Se observarmos a '''XL710-QDA2''' é '''PCIe 3.0 x8 (8 lanes)''' ou seja o barramento irá suportar:<br />
<br />
* '''8.0 GT/s * (128b/130b encoding) * 8 lanes = 63,01 Gbps'''<br />
<br />
O objetivo do '''LACP''' nesse caso, '''não seria alcançar os''' '''80 Gbps de capacidade''' em cada interface, mesmo porque cada barramento das interfaces é '''limitado em 63,01 Gbps''', mas manteremos um '''backup dos 40 Gbps'''. <br />
<br />
Nessa configuração teríamos teoricamente '''63,01 Gbps de entrada e 63,01 Gbps de saída'''. Mas para esse cenário precisaremos fazer uma coisa chamada '''CPU Affinity'''. Nesse caso colocaríamos um processador dedicado para cada interface de rede. É um cenário mais complexo do que com 1 processador apenas, inclusive necessitamos de olhar o datasheet da motherboard e identificar quais slots PCIe são diretamente controlados por qual CPU. Se temos a '''CPU0''' e '''CPU1''', uma interface precisará ficar no slot controlado pela '''CPU0''' e a outra interface no slot controlado pela '''CPU1''' e '''observar a quantidade de lanes no slot para ver se suporta a mesma quantidade de lanes da interface de rede'''.<br />
<br />
Falando um pouco sobre PPS (Packet Per Second) para calcular por exemplo 1 Gbps de tráfego na ethernet, a quantidade de PPS que o sistema precisaria suportar encaminhar teríamos: 1.000.000.000/8/1518 = 82.345 packets per second.<br />
<br />
Existe um comando no GNU/Linux para você saber se o seu equipamento com processadores físicos, conseguirá trabalhar com o '''CPU Affinity''':<br />
# cat /sys/class/net/<interface>/device/numa_node<br />
Se o resultado do comando acima for '''-1''' então esse equipamento não trabalhará com o '''CPU Affinity'''. Isso porque cada interface precisa estar sendo gerenciada por um node específico. Se são 2 processadores então o resultado deveria ser '''0 de CPU0''' ou '''1 de CPU1'''.<br />
<br />
A seguir veremos um exemplo de datasheet da '''motherboard S2600WF''':<br />
[[Arquivo:903px-S2600wf.png|nenhum|miniaturadaimagem|903x903px]]<br />
Se observarmos o datasheet acima veremos que temos o '''PCIe Riser #1''', '''Riser #2''' e '''Riser #3'''. Cada Riser possui slots PCIe que são gerenciados por determinada CPU. Se colocássemos as duas interfaces de rede nos '''slots do Riser #2''' e '''Riser #3''', estaríamos pendurando tudo apenas no '''processador 2'''. Isso foi apenas para mostrar a complexidade de quando usamos um equipamento '''NUMA''' e estamos somente escolhendo o hardware adequado. Ainda não chegamos na configuração do '''CPU Affinity'''.<br />
<br />
Para sabermos quais cores estão relacionados para uma determinada CPU, utilizamos os comandos abaixo:<br />
# cat /sys/devices/system/node/node0/cpulist<br />
0-7<br />
<br />
# cat /sys/devices/system/node/node1/cpulist<br />
8-15<br />
No exemplo acima a '''CPU0''' '''tem os cores de 0 a 7''' e a '''CPU1, os cores de 8 a 15''', ou seja, é um equipamento com '''16 cores'''.<br />
<br />
=== Tuning antes do CPU Affinity ===<br />
Também é importante, para aumento de performance, que seja '''desabilitado na BIOS o HT (Hyper Threading)'''.<br />
<br />
Antes de configurarmos algumas coisas no nosso ambiente, precisaremos instalar uma ferramenta importante para o nosso tuning; vamos instalar o pacote '''ethtool'''. Ele servirá para fazermos alguns ajustes nas nossas interfaces de rede. Alguns fabricantes podem não permitir certas alterações mas com as interfaces da Intel sempre obtive os resultados esperados. <br />
# apt install ethtool<br />
No nosso exemplo acima vimos que o equipamento possui '''16 cores''' sendo que '''8 cores por CPU'''. Então, para esse caso, faremos um ajuste nas interfaces para ficarem preparadas para receberem '''8 cores em cada através das IRQs'''. Usamos o parâmetro '''-l''' do '''ethtool''' para listar o '''Pre-set maximums combined''' da interface e o parâmetro '''-L''' para alterar esse valor. Façamos então a alteração:<br />
# ethtool -L enp5s0f0 combined 8<br />
# ethtool -L enp5s0f1 combined 8<br />
# ethtool -L enp6s0f0 combined 8<br />
# ethtool -L enp6s0f1 combined 8<br />
Com os comandos acima deixamos preparadas as interfaces para aceitarem '''8 cores em cada uma através das IRQs'''. <br />
<br />
Não podemos usar o programa '''irqbalance''' para o '''CPU Affinity''', pois este faz migração de contextos entre os cores e isso é ruim. Como no nosso exemplo estamos usando uma interface Intel, utilizaremos um script da própria Intel para realizar o '''CPU Affinity''' de forma mais fácil. Esse script se chama '''set_irq_affinity''' e vem acompanhado com os fontes do driver da interface. Ex.: '''[https://www.intel.com/content/www/us/en/download/18026/intel-network-adapter-driver-for-pcie-40-gigabit-ethernet-network-connections-under-linux.html Intel Network Adapter]'''<br />
<br />
=== Código do script '''set_irq_affinity''' ===<br />
#!/bin/bash<br />
# SPDX-License-Identifier: BSD-3-Clause<br />
# Copyright (c) 2015 - 2019, Intel Corporation<br />
#<br />
# Affinitize interrupts to cores<br />
#<br />
# typical usage is (as root):<br />
# set_irq_affinity -x local eth1 <eth2> <eth3><br />
# set_irq_affinity -s eth1<br />
#<br />
# to get help:<br />
# set_irq_affinity<br />
<br />
usage()<br />
{<br />
echo<br />
echo "Usage: option -s <interface> to show current settings only"<br />
echo "Usage: $0 [-x|-X] [all|local|remote [<node>]|one <core>|custom|<cores>] <interface> ..."<br />
echo " Options: "<br />
echo " -s Shows current affinity settings"<br />
echo " -x Configure XPS as well as smp_affinity"<br />
echo " -X Disable XPS but set smp_affinity"<br />
echo " [all] is the default value"<br />
echo " [remote [<node>]] can be followed by a specific node number"<br />
echo " Examples:"<br />
echo " $0 -s eth1 # Show settings on eth1"<br />
<br />
echo " $0 all eth1 eth2 # eth1 and eth2 to all cores"<br />
echo " $0 one 2 eth1 # eth1 to core 2 only"<br />
echo " $0 local eth1 # eth1 to local cores only"<br />
echo " $0 remote eth1 # eth1 to remote cores only"<br />
echo " $0 custom eth1 # prompt for eth1 interface"<br />
echo " $0 0-7,16-23 eth0 # eth1 to cores 0-7 and 16-23"<br />
echo<br />
exit 1<br />
}<br />
<br />
usageX()<br />
{<br />
echo "options -x and -X cannot both be specified, pick one"<br />
exit 1<br />
}<br />
<br />
if [ "$1" == "-x" ]; then<br />
XPS_ENA=1<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-s" ]; then<br />
SHOW=1<br />
echo Show affinity settings<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-X" ]; then<br />
if [ -n "$XPS_ENA" ]; then<br />
usageX<br />
fi<br />
XPS_DIS=2<br />
shift<br />
fi<br />
<br />
if [ "$1" == -x ]; then<br />
usageX<br />
fi<br />
<br />
if [ -n "$XPS_ENA" ] && [ -n "$XPS_DIS" ]; then<br />
usageX<br />
fi<br />
<br />
if [ -z "$XPS_ENA" ]; then<br />
XPS_ENA=$XPS_DIS<br />
fi<br />
<br />
SED=`which sed`<br />
if <nowiki>[[ ! -x $SED ]]</nowiki>; then<br />
echo " $0: ERROR: sed not found in path, this script requires sed"<br />
exit 1<br />
fi<br />
<br />
num='^[0-9]+$'<br />
<br />
# search helpers<br />
NOZEROCOMMA="s/^[0,]*//"<br />
# Vars<br />
AFF=$1<br />
shift<br />
<br />
case "$AFF" in<br />
remote) <nowiki>[[ $1 =~ $num ]]</nowiki> && rnode=$1 && shift ;;<br />
one) <nowiki>[[ $1 =~ $num ]]</nowiki> && cnt=$1 && shift ;;<br />
all) ;;<br />
local) ;;<br />
custom) ;;<br />
[0-9]*) ;;<br />
-h|--help) usage ;;<br />
"") usage ;;<br />
*) IFACES=$AFF && AFF=all ;; # Backwards compat mode<br />
esac<br />
<br />
# append the interfaces listed to the string with spaces<br />
while [ "$#" -ne "0" ] ; do<br />
IFACES+=" $1"<br />
shift<br />
done<br />
<br />
# for now the user must specify interfaces<br />
if [ -z "$IFACES" ]; then<br />
usage<br />
exit 2<br />
fi<br />
<br />
notfound()<br />
{<br />
echo $MYIFACE: not found<br />
exit 15<br />
}<br />
<br />
# check the interfaces exist<br />
for MYIFACE in $IFACES; do<br />
grep -q $MYIFACE /proc/net/dev || notfound<br />
done<br />
<br />
# support functions<br />
<br />
build_mask()<br />
{<br />
VEC=$core<br />
if [ $VEC -ge 32 ]<br />
then<br />
MASK_FILL=""<br />
MASK_ZERO="00000000"<br />
let "IDX = $VEC / 32"<br />
for ((i=1; i<=$IDX;i++))<br />
do<br />
MASK_FILL="${MASK_FILL},${MASK_ZERO}"<br />
done<br />
<br />
let "VEC -= 32 * $IDX"<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X%s" $MASK_TMP $MASK_FILL)<br />
else<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X" $MASK_TMP)<br />
fi<br />
}<br />
<br />
show_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
HINT=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/affinity_hint`<br />
printf "ACTUAL %s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf "HINT %s %d %s <- /proc/irq/$IRQ/affinity_hint\n" $IFACE $core $HINT<br />
IRQ_CHECK=`grep '[-,]' /proc/irq/$IRQ/smp_affinity_list`<br />
if [ ! -z $IRQ_CHECK ]; then<br />
printf " WARNING -- SMP_AFFINITY is assigned to multiple cores $IRQ_CHECK\n"<br />
fi<br />
if [ "$SMP_I" != "$HINT" ]; then<br />
printf " WARNING -- SMP_AFFINITY VALUE does not match AFFINITY_HINT \n"<br />
fi<br />
printf "NODE %s %d %s <- /proc/irq/$IRQ/node\n" $IFACE $core `cat /proc/irq/$IRQ/node`<br />
printf "LIST %s %d [%s] <- /proc/irq/$IRQ/smp_affinity_list\n" $IFACE $core `cat /proc/irq/$IRQ/smp_affinity_list`<br />
printf "XPS %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` ]; then<br />
echo "WARNING: xps rxqs not supported on $IFACE"<br />
else<br />
printf "XPSRXQs %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_rxqs\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` $IFACE $((n-1))<br />
fi<br />
printf "TX_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/tx_maxrate\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/tx_maxrate` $IFACE $((n-1))<br />
printf "BQLIMIT %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit` $IFACE $((n-1))<br />
printf "BQL_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_max\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_max` $IFACE $((n-1))<br />
printf "BQL_MIN %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_min\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_min` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` ]; then<br />
echo "WARNING: aRFS is not supported on $IFACE"<br />
else<br />
printf "RPSFCNT %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_flow_cnt\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` $IFACE $((n-1))<br />
fi<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` ]; then<br />
echo "WARNING: rps_cpus is not available on $IFACE"<br />
else<br />
printf "RPSCPU %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` $IFACE $((n-1))<br />
fi<br />
echo<br />
}<br />
<br />
set_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
printf "%s" $MASK > /proc/irq/$IRQ/smp_affinity<br />
printf "%s %d %s -> /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $MASK<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
if [ "$SMP_I" != "$MASK" ]; then<br />
printf " ACTUAL\t%s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf " WARNING -- SMP_AFFINITY setting failed\n"<br />
fi<br />
case "$XPS_ENA" in<br />
1)<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
2)<br />
MASK=0<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
*)<br />
esac<br />
}<br />
<br />
# Allow usage of , or -<br />
#<br />
parse_range () {<br />
RANGE=${@//,/ }<br />
RANGE=${RANGE//-/..}<br />
LIST=""<br />
for r in $RANGE; do<br />
# eval lets us use vars in {#..#} range<br />
<nowiki>[[ $r =~ '..' ]]</nowiki> && r="$(eval echo {$r})"<br />
LIST+=" $r"<br />
done<br />
echo $LIST<br />
}<br />
<br />
# Affinitize interrupts<br />
#<br />
doaff()<br />
{<br />
CORES=$(parse_range $CORES)<br />
ncores=$(echo $CORES | wc -w)<br />
n=1<br />
<br />
# this script only supports interrupt vectors in pairs,<br />
# modification would be required to support a single Tx or Rx queue<br />
# per interrupt vector<br />
<br />
queues="${IFACE}-.*TxRx"<br />
<br />
irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(for i in `ls -1 /sys/class/net/${IFACE}/device/msi_irqs | sort -n` ;do grep -w $i: /proc/interrupts | egrep -v 'fdir|async|misc|ctrl' | cut -f 1 -d :; done)<br />
[ -z "$irqs" ] && echo "Error: Could not find interrupts for $IFACE"<br />
<br />
if [ "$SHOW" == "1" ] ; then<br />
echo "TYPE IFACE CORE MASK -> FILE"<br />
echo "============================"<br />
else<br />
echo "IFACE CORE MASK -> FILE"<br />
echo "======================="<br />
fi<br />
<br />
for IRQ in $irqs; do<br />
[ "$n" -gt "$ncores" ] && n=1<br />
j=1<br />
# much faster than calling cut for each<br />
for i in $CORES; do<br />
[ $((j++)) -ge $n ] && break<br />
done<br />
core=$i<br />
if [ "$SHOW" == "1" ] ; then<br />
show_affinity<br />
else<br />
set_affinity<br />
fi<br />
((n++))<br />
done<br />
}<br />
<br />
# these next 2 lines would allow script to auto-determine interfaces<br />
#[ -z "$IFACES" ] && IFACES=$(ls /sys/class/net)<br />
#[ -z "$IFACES" ] && echo "Error: No interfaces up" && exit 1<br />
<br />
# echo IFACES is $IFACES<br />
<br />
CORES=$(</sys/devices/system/cpu/online)<br />
[ "$CORES" ] || CORES=$(grep ^proc /proc/cpuinfo | cut -f2 -d:)<br />
<br />
# Core list for each node from sysfs<br />
node_dir=/sys/devices/system/node<br />
for i in $(ls -d $node_dir/node*); do<br />
i=${i/*node/}<br />
corelist[$i]=$(<$node_dir/node${i}/cpulist)<br />
done<br />
<br />
for IFACE in $IFACES; do<br />
# echo $IFACE being modified<br />
<br />
dev_dir=/sys/class/net/$IFACE/device<br />
[ -e $dev_dir/numa_node ] && node=$(<$dev_dir/numa_node)<br />
[ "$node" ] && [ "$node" -gt 0 ] || node=0<br />
<br />
case "$AFF" in<br />
local)<br />
CORES=${corelist[$node]}<br />
;;<br />
remote)<br />
[ "$rnode" ] || { [ $node -eq 0 ] && rnode=1 || rnode=0; }<br />
CORES=${corelist[$rnode]}<br />
;;<br />
one)<br />
[ -n "$cnt" ] || cnt=0<br />
CORES=$cnt<br />
;;<br />
all)<br />
CORES=$CORES<br />
;;<br />
custom)<br />
echo -n "Input cores for $IFACE (ex. 0-7,15-23): "<br />
read CORES<br />
;;<br />
[0-9]*)<br />
CORES=$AFF<br />
;;<br />
*)<br />
usage<br />
exit 1<br />
;;<br />
esac<br />
<br />
# call the worker function<br />
doaff<br />
done<br />
<br />
# check for irqbalance running<br />
IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?`<br />
if [ "$IRQBALANCE_ON" == "0" ] ; then<br />
echo " WARNING: irqbalance is running and will"<br />
echo " likely override this script's affinitization."<br />
echo " Please stop the irqbalance service and/or execute"<br />
echo " 'killall irqbalance'"<br />
exit 2<br />
fi<br />
<br />
=== CPU Affinity ===<br />
Agora que preparamos as interfaces, façamos os apontamentos dos cores da seguinte forma. Vamos supor que colocamos o script em '''/root/scripts''':<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
<br />
=== Mais alguns tunings ===<br />
Vamos fazer mais alguns ajustes nas interfaces com o '''ethtool'''. Dessa vez vamos aumentar os '''Rings RX''' e '''TX'''. Mas antes vamos listar os valores que podemos usar:<br />
# ethtool -g enp5s0f0<br />
Ring parameters for enp5s0f0:<br />
Pre-set maximums:<br />
RX: 4096<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 4096<br />
Current hardware settings:<br />
RX: 512<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 512<br />
Acima vemos que o '''valor máximo é de 4096 tanto para TX''', '''quanto para RX''' mas está '''configurado para 512 em RX e TX'''. Façamos então:<br />
# ethtool -G enp5s0f0 rx 4096 tx 4096<br />
# ethtool -G enp5s0f1 rx 4096 tx 4096<br />
# ethtool -G enp6s0f0 rx 4096 tx 4096<br />
# ethtool -G enp6s0f1 rx 4096 tx 4096<br />
Vamos desabilitar as seguintes options das interfaces: '''TSO''', '''GRO''' e '''GSO'''. <br />
# ethtool -K enp5s0f0 tso off gro off gso off<br />
# ethtool -K enp5s0f1 tso off gro off gso off<br />
# ethtool -K enp6s0f0 tso off gro off gso off<br />
# ethtool -K enp6s0f1 tso off gro off gso off<br />
Aumentaremos o '''txqueuelen''' para '''10000''':<br />
# ip link set enp5s0f0 txqueuelen 10000<br />
# ip link set enp5s0f1 txqueuelen 10000<br />
# ip link set enp6s0f0 txqueuelen 10000<br />
# ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Salvando a configuração e criando o LACP ===<br />
Tudo que fizemos até o momento será perdido no próximo reboot do sistema, então faremos com que esses comandos sejam executados sempre que o sistema iniciar. Para isso vamos deixar o nosso arquivo '''/etc/network/interfaces''' configurado conforme nosso diagrama, usando '''LACP''' e executando nossos comandos anteriores.<br />
<br />
Antes precisaremos instalar o pacote '''ifenslave''' para que o bonding funcione:<br />
# apt install ifenslave<br />
# modprobe bonding<br />
# echo "bonding" >> /etc/modules<br />
Abaixo o nosso '''/etc/network/interfaces''' já com todas as configurações que fizemos anteriormente e seguindo nosso diagrama de exemplo:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto bond0<br />
iface bond0 inet static<br />
bond-slaves enp5s0f0 enp5s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 10.0.10.172/24<br />
gateway 10.0.10.1<br />
pre-up /usr/sbin/ethtool -L enp5s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp5s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
pre-up /usr/sbin/ethtool -G enp5s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp5s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp5s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp5s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp5s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp5s0f1 txqueuelen 10000<br />
<br />
auto bond1<br />
iface bond1 inet static<br />
bond-slaves enp6s0f0 enp6s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 192.168.0.1/24<br />
pre-up /usr/sbin/ethtool -L enp6s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp6s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
pre-up /usr/sbin/ethtool -G enp6s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp6s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp6s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp6s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp6s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Atualizando o Kernel ===<br />
Colocaremos o '''kernel do backports'''. Para isso deixe o seu '''/etc/apt/sources''' conforme abaixo e rode os comandos na sequência:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
<br />
# apt update<br />
# apt install -t bullseye-backports linux-image-amd64<br />
# reboot<br />
<br />
=== Protegendo contra static loop e preparando o ambiente do CGNAT ===<br />
O '''static loop''' é algo que, definitivamente, pode derrubar toda a sua operação se não for devidamente tratado e pode ser facilmente explorado por pessoas mal intencionadas. A causa do problema é uma rota estática para um prefixo IP (seja IPv4 ou IPv6), que aponta para um next-hop e nesse destino não existe nenhuma informação sobre o prefixo IP na tabela de rotas local, obrigando o pacote a retornar para o seu gateway default e ficando nesse loop até que '''expire o TTL (Time To Live) do pacote'''. Isso ocorre muito nos casos em que temos concentradores PPPoE (BNG) e caixas CGNAT como esta que estaremos fazendo. Em '''[[Recomendações sobre Mitigação DDoS]]''' temos outras dicas de segurança sobre o assunto '''DDoS'''.<br />
<br />
Crie um arquivo /etc/rc.local e dentro colocaremos algumas coisas como as blackholes para cada prefixo IPv4 público que usaremos no nosso servidor de exemplo e rotas de retorno para o nosso BNG:<br />
# > /etc/rc.local<br />
# chmod +x /etc/rc.local<br />
Dentro teremos:<br />
#!/bin/sh -e<br />
/usr/sbin/ip route add blackhole 198.18.0.0/27 metric 254<br />
/usr/sbin/ip route add 100.64.0.0/22 via 192.168.0.2<br />
No exemplo acima estamos colocando em '''blackhole''' o nosso prefixo IPv4 público deste tutorial que é o '''198.18.0.0/27''' e adicionando uma rota de retorno do prefixo '''100.64.0.0/22''' usado no nosso '''BNG''' para o '''next-hop 192.168.0.2'''.<br />
<br />
=== Redução dos tempos de timeouts e outros ajustes ===<br />
Os tempos padrões dos '''timeouts''' de '''tcp''' e '''udp''' são altos para o nosso sistema de CGNAT, ainda mais quando estamos '''diminuindo a quantidade de portas tcp/udp por assinante''' e com isso podemos rapidamente estourar esse limite, fazendo com que o sistema pare de funcionar. Abaixo estou colocando os valores que sempre usei e não percebi problemas, mas você pode ajustar conforme achar mais prudente. Adicionaremos as configurações abaixo também no nosso '''/etc/rc.local''': <br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_sent<br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_recv<br />
echo 86400 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_fin_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_last_ack<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_max_retrans<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_unacknowledged<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout<br />
echo 180 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_icmp_timeout<br />
echo 600 > /proc/sys/net/netfilter/nf_conntrack_generic_timeout<br />
Em '''/etc/sysctl.conf''' adicionaremos:<br />
net.core.default_qdisc=fq<br />
net.ipv4.tcp_congestion_control=bbr<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.ipv4.conf.all.forwarding=1<br />
net.netfilter.nf_conntrack_helper=1<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
As configurações acima melhoram o uso de memória, habilita o encaminhamento dos pacotes e aumenta a quantidade máxima de '''conntracks''' do sistema para '''4096000'''.<br />
<br />
'''Se o conntrack estourar, seu CGNAT terá problemas e causará indisponibilidades'''. Para consultar a quantidade de conntracks em uso:<br />
# cat /proc/sys/net/netfilter/nf_conntrack_count<br />
Para listar as '''conntracks''':<br />
# cat /proc/net/nf_conntrack<br />
<br />
=== Ajustando a data e horário do sistema ===<br />
Uma tarefa muito importante a ser feita nos servidores, é garantir que o horário e data estejam corretos e para isso usaremos o programa '''chrony'''. Eu prefiro usar sempre horário UTC nos servidores e fazer a conversão quando necessário:<br />
# apt install chrony<br />
Basta copiar e colar os comandos abaixo, para configurar o '''chrony''':<br />
# cat << EOF > /etc/chrony/chrony.conf<br />
confdir /etc/chrony/conf.d<br />
sourcedir /run/chrony-dhcp<br />
sourcedir /etc/chrony/sources.d<br />
keyfile /etc/chrony/chrony.keys<br />
driftfile /var/lib/chrony/chrony.drift<br />
ntsdumpdir /var/lib/chrony<br />
logdir /var/log/chrony<br />
maxupdateskew 100.0<br />
rtcsync<br />
makestep 1 3<br />
leapsectz right/UTC<br />
EOF<br />
<br />
# cat << EOF > /etc/chrony/sources.d/nic.sources<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
EOF<br />
Aqui reiniciamos o serviço e configuramos o '''timezone''':<br />
# systemctl restart chronyd.service<br />
# timedatectl set-timezone "UTC"<br />
<br />
=== Habilitando ALG (Application Layer Gateway) ===<br />
No arquivo '''/etc/modules''' adicionaremos os módulos que usaremos no nosso CGNAT, inclusive os ALGs'''.''' Sem eles alguns serviços, ainda muito utilizados, apresentarão problemas.<br />
<br />
Em '''/etc/modules''' adicionaremos mais os módulos abaixo:<br />
nf_conntrack<br />
nf_nat_pptp<br />
nf_nat_h323<br />
nf_nat_sip<br />
nf_nat_irc<br />
nf_nat_ftp<br />
nf_nat_tftp<br />
<br />
=== Preparando ambiente e gerador de regras de CGNAT ===<br />
Antes de começarmos nossas regras de CGNAT precisaremos de alguns pacotes:<br />
# apt install python3-pip nftables<br />
# pip install ipaddress<br />
Vamos precisar também de um gerador de regras de CGNAT para '''nftables'''. Porque criar as regras manualmente não é uma tarefa rápida e para isso usaremos um programa em python criado por '''José Beiriz''' e disponibilizado aqui: '''[https://github.com/Beiriz/GRCN GRCN]'''<br />
<br />
Caso não consigam baixar por algum motivo o '''GRCN''', aqui '''<nowiki>https://github.com/gondimcodes/GRCN</nowiki>''' também pode ser encontrado o script '''cgnat-nft.py.'''<br />
<br />
Nosso sistema de regras CGNAT será dividido em 2 partes:<br />
<br />
* O script base que colocaremos em '''/root/scripts''' chamado de '''frw-nft.sh'''. Esse script conterá as regras básicas do CGNAT e este incluirá a chamada para os outros arquivos de regras propriamente ditos do CGNAT. <br />
<br />
* Essa outra parte é composta pelos arquivos de regras de CGNAT, onde são feitas as traduções de IPs privados '''100.64.0.0/10 (Shared Address Space - RFC6598)''', para os '''IPs públicos'''. A seguir o '''frw-nft.sh''':<br />
<br />
Nosso script de CGNAT base '''/root/scripts/frw-nft.sh''':<br />
#!/usr/sbin/nft -f<br />
# limpa todas as regras da memoria<br />
flush ruleset<br />
<br />
# regras base para o CGNAT<br />
add table ip nat<br />
add chain ip nat POSTROUTING { type nat hook postrouting priority 100; policy accept; }<br />
<br />
add chain ip nat CGNATOUT<br />
<br />
# libera o proprio CGNAT para acessar a Internet - para atualizacoes por exemplo<br />
add rule ip nat POSTROUTING oifname "bond0" ip saddr 10.0.10.172 counter snat to 198.18.0.0<br />
<br />
# faz o jump para as regras de CGNAT<br />
add rule ip nat POSTROUTING oifname "bond0" counter jump CGNATOUT<br />
<br />
'''# carrega os arquivos de regras de CGNAT'''<br />
include "/root/scripts/cgnat-0-31.conf"<br />
A última linha do script acima, em '''negrito''', é o arquivo de regras CGNAT que iremos gerar e será chamado pelo script quando for executado.<br />
<br />
Após a criação do script, alteramos a permissão dele para ficar como executável e adicionamos ele em nosso '''/etc/rc.local''':<br />
# chmod 700 /root/scripts/frw-nft.sh<br />
# echo "/root/scripts/frw-nft.sh" >> /etc/rc.local<br />
<br />
=== Gerando nossas regras de CGNAT ===<br />
Colocaremos o script '''cgnat-nft.py''' em '''/root/scripts/'''. Como estamos trabalhando '''no modelo determinístico de 1/32''', basta pegarmos nosso bloco privado '''100.64.0.0/22 (1024 IPs)''' e nosso bloco público '''198.18.0.0/27 (32 IPs)''' e executarmos em linha de comando:<br />
# cd /root/scripts<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
Se digitar apenas '''./cgnat-nft.py''' será apresentado um help dos parâmetros mas é bem simples o seu uso. No comando acima '''temos o número 0 como índice'''. Muito cuidado com o índice, porque ele é muito importante para a performance e para cada novo arquivo gerado, esse índice precisará ser incrementado. O comando acima criará automaticamente o arquivo chamado '''cgnat-0-31.conf''', aquele mesmo visto no script base sendo carregado com o '''include'''. Onde esse '''0-31''' quer dizer que nesse arquivo '''os índices vão de 0 a 31'''. Se for gerar um novo arquivo com o comando acima, o próximo índice a ser usado seria o '''32'''. Por exemplo:<br />
# ./cgnat-nft.py '''32''' 198.18.0.32/27 100.64.4.0/22 1/32<br />
Esse comando acima criará novas regras no arquivo chamado '''cgnat-32-63.conf''', na sequência inclua esse novo arquivo dentro do '''/root/scripts/frw-nft.sh''' e '''execute o /root/scripts/frw-nft.sh novamente''' para carregar as novas regras. A seguir daremos uma olhada nas regras geradas nesses arquivos.<br />
<br />
=== Executando o gerador de regras ===<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
[[Arquivo:1022px-Grcn.png|nenhum|miniaturadaimagem|1022x1022px]]<br />
Após teclar '''ENTER''' será gerado o arquivo '''cgnat-0-31.conf''' com as regras conforme a tela abaixo de exemplo:<br />
[[Arquivo:1027px-Regras01.png|nenhum|miniaturadaimagem|1027x1027px]]<br />
Na tela abaixo se observarmos o '''retângulo vermelho''' veremos a regra que faz o '''NAT de tudo que não for TCP ou UDP''' e por fim a regra que faz o '''jump''' '''de tudo que for origem 100.64.0.0/27''' para o '''CGNATOUT_0''' onde esse '''0 é o índice'''.<br />
[[Arquivo:1029px-Regras02.png|nenhum|miniaturadaimagem|1029x1029px]]<br />
<br />
=== Explicando a função dos índices ===<br />
O sistema de avaliação de regras de filtros de pacotes e NAT no GNU/Linux é do tipo '''First Match Win''', o que significa que a pesquisa das regras se encerra quando o sistema encontra uma regra que dê match. O sistema fica muito mais otimizado e performático quando quebramos as regras e separamos em '''CHAINS''' e é aí que entram os '''índices'''. Porque as '''CHAINS''' não podem ter o mesmo nome, senão não haveria separação das regras. A seguir veremos por exemplo que quando houver um pacote relacionado com o prefixo de origem '''100.64.0.0/27''', este será encaminhado para a chain '''CGNATOUT_0''', que é onde estão as regras de CGNAT para esse bloco IP. Desse jeito a checagem para esse prefixo não percorre todas as regras de NAT contidas na memória.<br />
[[Arquivo:Regras03.png|nenhum|miniaturadaimagem|1034x1034px]]<br />
<br />
=== Novo script gerador de regras nftables com suporte a netmap ===<br />
Com a versão nova que virá do '''Debian''', a '''versão 12 (Bookworm)''', teremos também uma versão nova do '''nftables''' '''1.0.6''' e essa versão já suporta o equivalente ao '''netmap''' que temos no '''Mikrotik''' e com isso teremos menos regras na memória e provavelmente mais performance. O sistema novo conta também com o '''kernel 6.1.27''' que possui diversas '''melhorias na pilha tcp/ip'''. Para aqueles que já quiserem testar nesse novo ambiente, fiz uma modificação no script python mostrado anteriormente, para gerar regras nesse novo formato e um arquivo tabela com o relacionamento de portas e IPs para quebra de sigilo tecnológico. Aqui '''<nowiki>https://github.com/gondimcodes/GRCN</nowiki>''' o novo código e estarei solicitando ao '''José Beiriz''' para incorporá-lo no '''GRCN'''.<br />
Para gerar as regras é só executar da mesma maneira. Exemplo:<pre><br />
# ./cgnat-nft-netmap.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
</pre>Abaixo exemplos de como ficam as novas regras e na memória:<br />
[[Arquivo:Nftables netmap1.png|nenhum|commoldura|926x926px]]<br />
[[Arquivo:Nftables netmap2.png|nenhum|commoldura|929x929px]]<br />
Exemplo do arquivo '''tabela-0-31.txt''' que foi gerado:<br />
[[Arquivo:Nftables netmap3.png|nenhum|commoldura|963x963px]]<br />
<br />
=== Simulando um acesso do cliente e observando os resultados ===<br />
Para testar as regras, foi criado um ambiente virtual de laboratório usando um '''Proxmox''' e criando 3 VMs: '''CGNAT''', '''BNG''' e '''CLIENTE'''. Do router de testes capturei os pacotes para demonstrar como funciona o CGNAT. A seguir teremos o acesso por parte do cliente e a captura dos pacotes somente para uma '''POC (Proof of Concept)''', para demonstrar que o CGNAT está funcionando e alocando a porta, dentro do range de portas, corretamente para um determinado cliente.<br />
<br />
Abaixo temos um exemplo de captura bem simples de pacote mostrando que o '''IP 198.18.0.0''' com '''porta origem''' '''6767/TCP''' acessou o '''200.147.41.220 na porta 443/TCP''', um acesso para o site do '''UOL'''.<br />
[[Arquivo:Cgnat sniffer.png|nenhum|miniaturadaimagem|1039x1039px]]<br />
Se olharmos os dados marcados acima e procurarmos pelo IP '''198.18.0.0 e porta 6767''' no nosso arquivo de configuração do CGNAT, '''acharemos o IP 100.64.0.2''' '''que utiliza o range de portas entre 5056 e 7071'''. Abaixo o nosso arquivo de regras de CGNAT para comprovar o range de portas utilizados.<br />
[[Arquivo:Regras5.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Monitorando o tráfego em tempo real ===<br />
Monitorando o '''tráfego Mbps/PPS''' com a ferramenta '''bmon'''. Para instalar o software no Debian basta fazer: <br />
# apt install bmon<br />
Para monitorar as interfaces faríamos algo assim onde '''-b''' para '''bits/s''' e o '''-p''' para '''selecionar as interfaces que quer monitorar'''. Para monitorar nosso '''bond0''' e '''bond1''' o comando seria esse abaixo:<br />
# bmon -b -p bond0,bond1<br />
Abaixo uma tela de exemplo do '''bmon''' em execução:<br />
[[Arquivo:Bmon cgnat.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
== CGNAT no Mikrotik RouterOS ==<br />
Uma boa opção para caixa CGNAT com custo x benefício acessível seria uma '''CCR1036-8G-2S+''' onde se for configurada somente para fazer '''CGNAT''', com o '''mínimo de regras de filtro''' e '''Fasttrack habilitado''', já alcancei '''13 Gbps de tráfego ou 26 Gbps agregado''' fazendo um '''bonding com as 2 interfaces ópticas de 10Gbps'''.<br />
<br />
Essa imagem abaixo foi retirada do '''datasheet da CCR1036-8G-2S+''':<br />
[[Arquivo:Datasheet ccr1036.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Configurando o sistema ===<br />
Instale um '''Mikrotik RouterOS do zero''', procure utilizar a '''versão mais estável possível'''. Como não utilizei ainda em produção o RouterOS 7.x, '''sugiro utilizar a versão 6.48.6 Long-term''', que até o momento, é a versão considerada mais estável. O processo de configurar um '''CGNAT Determinístico no Mikrotik RouterOS''' será bem mais simples que no '''Debian GNU/Linux''' mas a capacidade alcançada com o '''GNU/Linux''' será bem superior ao visto aqui.<br />
<br />
=== Sobre Fasttrack ===<br />
O '''Fasttrack''' é um recurso muito importante que aumentará a performance da sua caixa CGNAT, '''acelerando o encaminhamento de pacotes''' e '''diminuindo o consumo de CPU'''. Neste momento não faremos isso. Quando chegarmos no processo de criação das regras de CGNAT, ele será habilitado e será mostrado quais as regras que fazem isso.<br />
<br />
=== Configurando o bonding ===<br />
Como usaremos as '''duas portas de 10GbE sfp+ da CCR''', utilizaremos vlans para separar a rede que se comunicará com a Internet, da rede com o BNG. A seguir veremos como deixar o nosso bonding. Na sequência configuramos nossas vlans de entrada e saída e em cima delas '''os IPs do diagrama''', como fizemos com o Debian. Vamos definir a '''vlan 101''' '''para a interface que fará a comunicação com a Internet''' e '''por onde será feito o CGNAT''' e a '''vlan 102 que fará a comunicação com o BNG'''.<br />
[[Arquivo:Cgnat mk1.png|nenhum|miniaturadaimagem|1043x1043px]]<br />
[[Arquivo:Cgnat mk2.png|nenhum|miniaturadaimagem|1237x1237px]]<br />
<br />
=== Configurando os IPs e rotas ===<br />
O objetivo deste artigo é ser bem simples para entendermos os conceitos e por isso estamos utilizando '''rotas estáticas''' e não estamos envolvendo outros protocolos como o '''OSPF'''. Nada impediria de utilizar a mesma técnica apresentada aqui em um cenário com '''OSPF''', por exemplo.<br />
<br />
A seguir veremos que na '''vlan-101-borda''' configuramos o '''IP 10.0.10.172/24''' e na '''vlan-102-bng''' configuramos o '''IP 192.168.0.1/24'''.<br />
<br />
Como rotas criamos uma '''default route''' apontando para o '''IP 10.0.10.1''', criamos uma rota para '''100.64.0.0/22''' com '''next-hop 192.168.0.2''' e para nos '''protegermos de static loop''' teremos nossas rotas de '''blackhole''' quando formos gerar as regras de CGNAT.<br />
<br />
Na imagem aparece como '''unreachable''' porque esse equipamento, que está sendo usado como lab, não está conectado em uma switch.<br />
[[Arquivo:Cgnat mk3.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
=== Recomendações de segurança ===<br />
<br />
* Utilize credenciais de acesso '''com senhas fortes''', não esqueça o login admin sem senha (padrão no Mikrotik RouterOS).<br />
<br />
* Desabilite todos os serviços que não for utilizar e os que ficarem abertos, especifique neles o acesso apenas da sua rede de gerência. Não deixe qualquer serviço aberto para a Internet.<br />
<br />
* Habilite o '''TCP SynCookies'''.<br />
<br />
Procure criar suas regras de filtros de pacotes sempre na '''Table Raw''', ela não agride tanto a performance do equipamento mas '''necessita de muita atenção''' porque ela pode afetar os acessos dos assinantes. Isso porque uma regra genérica demais será analisada tanto com destino a caixa, quanto destino ao cliente e o mesmo pode ocorrer no sentido inverso, do cliente para a Internet.<br />
[[Arquivo:Cgnat mk4.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Acertando data e hora ===<br />
Configure o '''NTP client da caixa''' e mantenha a data e horário sincronizados.<br />
[[Arquivo:Cgnat mk5.png|nenhum|miniaturadaimagem|1042x1042px]]<br />
<br />
=== Criando as regras de CGNAT ===<br />
Para simplificar nossa vida, '''Rudimar Remontti''' criou em seu blog, um sistema para gerar regras de '''CGNAT Determinístico''' de forma simples e performática, '''utilizando regras netmap da Mikrotik'''. Para tanto o link é este:<br />
<br />
'''https://cgnat.remontti.com.br/'''<br />
<br />
O sistema é bem completo, simples, irá gerar as '''regras de CGNAT''' e nossas '''blackholes''' para '''bloqueio de static loop'''. Também no final teremos uma '''tabela de associação''' que devemos guardar para fazer as quebras de sigilo solicitadas nos Ofícios Judiciais.<br />
<br />
Ao acessar o site e seguindo o nosso diagrama completaremos as informações conforme mostrado a seguir.<br />
[[Arquivo:Cgnat remontti1.png|nenhum|miniaturadaimagem|1047x1047px]]<br />
O site irá gerar automaticamente os comandos de onde faremos uma cópia e executaremos no nosso equipamento '''Mikrotik RouterOS'''.<br />
[[Arquivo:Cgnat remontti2.png|nenhum|miniaturadaimagem|1049x1049px]]<br />
No final da página é gerado uma tabela do mapeamento das portas, isso deve ser salvo como documento importante pois será usado para quebra de sigilo tecnológico.<br />
[[Arquivo:Cgnat remontti3.png|nenhum|miniaturadaimagem|1052x1052px]]<br />
O conceito é o mesmo, quebrar as regras em blocos menores para chegarmos no nosso '''First Match Win mais rápido''' e não termos que percorrer todas as regras em memória.<br />
[[Arquivo:Cgnat remontti4.png|nenhum|miniaturadaimagem|1053x1053px]]<br />
Abaixo como ficaram as regras que habilita o '''Fasttrack''' no nosso equipamento, aumentando em muito a performance de encaminhamento dos pacotes.<br />
[[Arquivo:Cgnat mk6.png|nenhum|miniaturadaimagem|1056x1056px]]<br />
<br />
== Conclusão ==<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=CGNAT_na_pratica&diff=3521CGNAT na pratica2023-07-06T05:38:11Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
[[Arquivo:773px-Cgnat nic br.jpg|nenhum|miniaturadaimagem|773x773px]]<br />
<br />
==Objetivo==<br />
Com o esgotamento do IPv4 mundialmente, precisamos tomar algumas providências para que a Internet não pare. As que vejo de imediatas são: '''IPv6''' e '''CGNAT (Carrier Grade NAT)'''. O '''IPv6''' é a real solução para os problemas de esgotamento e o CGNAT seria a "gambiarra" necessária para continuar com o IPv4 até que a Internet esteja 100% em IPv6. Nesse artigo será explicado como montar uma caixa CGNAT Determinística usando '''GNU/Linux''' e '''Mikrotik RouterOS'''. Esse artigo foi baseado no treinamento da '''Semana de Capacitação''' do '''NIC.br''' e que pode ser encontrado com o título '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT''' [https://semanacap.bcp.nic.br/6-online/ '''aqui'''] como palestra e material de apoio e o vídeo do treinamento no '''Youtube''' '''[https://www.youtube.com/watch?v=1q7J3NkQVSc aqui]'''.<br />
<br />
== Diagrama ==<br />
[[Arquivo:776px-Cgnat diagrama2.png|esquerda|miniaturadaimagem|776x776px]]<br />
No '''BNG''' é configurado uma '''PBR (Policy Based Routing)''' onde apenas IPs do bloco '''100.64.0.0/22''' serão roteados diretamente para a '''caixa CGNAT'''. Qualquer IPv4 público ou IPv6, serão roteados diretamente para a '''Borda'''. Isso evita processamento e tráfego desnecessário na '''caixa CGNAT'''. <br />
<br />
No diagrama ao lado a linha '''amarela''' simboliza o tráfego do bloco '''100.64.0.0/22''' indo para o '''CGNAT'''. A linha '''vermelha''' seria o tráfego já traduzido para um IP da rede '''198.18.0.0/27''' e encaminhado para a '''Borda'''. A linha '''verde''' é o tráfego mais limpo, sem "gambiarras" e o real objetivo que devemos seguir para uma '''Internet''' melhor usando IPv6.<br />
<br />
A '''Borda''' é um equipamento onde podemos inserir algumas regras de filtros de pacotes stateless para filtrar alguns pacotes indesejados como por exemplo: determinados spoofings e BOGONs. Também onde serão feitas ACLs para filtros BGP. '''Ação 1''' e '''2''' do '''MANRS'''.<br />
<br />
O '''Cliente''' nesse diagrama aparece conectado com o '''IPv4''' de CGNAT '''100.64.0.2''' e '''IPv6 2001:0db8:f18:0:a941:6164:1a79:c0f3'''. Todo o acesso IPv4 desse cliente e nesse exemplo, para a Internet, sairá com o IP '''198.18.0.0''' usando as portas entre '''5056''' e '''7071''', conforme mostraremos no script gerador de regras de CGNAT.<br />
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br />
<br />
<br />
<br />
== CGNAT no GNU/Linux ==<br />
<br />
=== Hardware e Sistema que utilizaremos no GNU/Linux ===<br />
* 2x Intel® Xeon® Silver 4215R Processor (3.20 GHz, 11M Cache, 8 núcleos/16 threads). Ambiente NUMA (non-uniform memory access).<br />
* 32Gb de ram.<br />
* 2x SSD 240 Gb RAID1.<br />
* 2x Interfaces de rede Intel XL710-QDA2 (2 portas de 40 Gbps).<br />
* GNU/Linux Debian 11 (Bullseye).<br />
<br />
Vamos configurar um LACP com as duas portas de cada interface, para que possamos ter um backup, caso algum módulo apresente algum problema. Seu ambiente de produção pode ser diferente e por isso precisamos ter alguns cuidados na hora de montarmos o conjunto de hardware e não obtermos surpresas.<br />
<br />
1º Verifique algumas especificações da interface de rede que será usada. Por exemplo a '''Intel XL710-QDA2''':<br />
<br />
* 2 portas de 40 Gbps.<br />
* PCIe 3.0 x8 (8.0 GT/s).<br />
<br />
Com essa informação seu equipamento não poderá possuir slots PCIe inferiores a esta especificação, caso contrário terá problemas de desempenho.<br />
<br />
Você também precisa estar atento para as limitações de barramento por '''versão''' x '''lane''' ('''x1'''):<br />
<br />
* PCIe 1.0/1.1 - 2.5 GT/s - (8b/10b encoding) - 2 Gbps.<br />
* PCIe 2.0/2.1 - 5.0 GT/s - (8b/10b encoding) - 4 Gbps.<br />
* PCIe 3.0/3.1 - 8.0 GT/s - (128b/130b encoding) - ~7,88 Gbps.<br />
* PCIe 4.0 - 16 GT/s - (128b/130b encoding) - ~15,76 Gbps.<br />
<br />
=== Calculando a capacidade ===<br />
Se observarmos a '''XL710-QDA2''' é '''PCIe 3.0 x8 (8 lanes)''' ou seja o barramento irá suportar:<br />
<br />
* '''8.0 GT/s * (128b/130b encoding) * 8 lanes = 63,01 Gbps'''<br />
<br />
O objetivo do '''LACP''' nesse caso, '''não seria alcançar os''' '''80 Gbps de capacidade''' em cada interface, mesmo porque cada barramento das interfaces é '''limitado em 63,01 Gbps''', mas manteremos um '''backup dos 40 Gbps'''. <br />
<br />
Nessa configuração teríamos teoricamente '''63,01 Gbps de entrada e 63,01 Gbps de saída'''. Mas para esse cenário precisaremos fazer uma coisa chamada '''CPU Affinity'''. Nesse caso colocaríamos um processador dedicado para cada interface de rede. É um cenário mais complexo do que com 1 processador apenas, inclusive necessitamos de olhar o datasheet da motherboard e identificar quais slots PCIe são diretamente controlados por qual CPU. Se temos a '''CPU0''' e '''CPU1''', uma interface precisará ficar no slot controlado pela '''CPU0''' e a outra interface no slot controlado pela '''CPU1''' e '''observar a quantidade de lanes no slot para ver se suporta a mesma quantidade de lanes da interface de rede'''.<br />
<br />
Falando um pouco sobre PPS (Packet Per Second) para calcular por exemplo 1 Gbps de tráfego na ethernet, a quantidade de PPS que o sistema precisaria suportar encaminhar teríamos: 1.000.000.000/8/1518 = 82.345 packets per second.<br />
<br />
Existe um comando no GNU/Linux para você saber se o seu equipamento com processadores físicos, conseguirá trabalhar com o '''CPU Affinity''':<br />
# cat /sys/class/net/<interface>/device/numa_node<br />
Se o resultado do comando acima for '''-1''' então esse equipamento não trabalhará com o '''CPU Affinity'''. Isso porque cada interface precisa estar sendo gerenciada por um node específico. Se são 2 processadores então o resultado deveria ser '''0 de CPU0''' ou '''1 de CPU1'''.<br />
<br />
A seguir veremos um exemplo de datasheet da '''motherboard S2600WF''':<br />
[[Arquivo:903px-S2600wf.png|nenhum|miniaturadaimagem|903x903px]]<br />
Se observarmos o datasheet acima veremos que temos o '''PCIe Riser #1''', '''Riser #2''' e '''Riser #3'''. Cada Riser possui slots PCIe que são gerenciados por determinada CPU. Se colocássemos as duas interfaces de rede nos '''slots do Riser #2''' e '''Riser #3''', estaríamos pendurando tudo apenas no '''processador 2'''. Isso foi apenas para mostrar a complexidade de quando usamos um equipamento '''NUMA''' e estamos somente escolhendo o hardware adequado. Ainda não chegamos na configuração do '''CPU Affinity'''.<br />
<br />
Para sabermos quais cores estão relacionados para uma determinada CPU, utilizamos os comandos abaixo:<br />
# cat /sys/devices/system/node/node0/cpulist<br />
0-7<br />
<br />
# cat /sys/devices/system/node/node1/cpulist<br />
8-15<br />
No exemplo acima a '''CPU0''' '''tem os cores de 0 a 7''' e a '''CPU1, os cores de 8 a 15''', ou seja, é um equipamento com '''16 cores'''.<br />
<br />
=== Tuning antes do CPU Affinity ===<br />
Também é importante, para aumento de performance, que seja '''desabilitado na BIOS o HT (Hyper Threading)'''.<br />
<br />
Antes de configurarmos algumas coisas no nosso ambiente, precisaremos instalar uma ferramenta importante para o nosso tuning; vamos instalar o pacote '''ethtool'''. Ele servirá para fazermos alguns ajustes nas nossas interfaces de rede. Alguns fabricantes podem não permitir certas alterações mas com as interfaces da Intel sempre obtive os resultados esperados. <br />
# apt install ethtool<br />
No nosso exemplo acima vimos que o equipamento possui '''16 cores''' sendo que '''8 cores por CPU'''. Então, para esse caso, faremos um ajuste nas interfaces para ficarem preparadas para receberem '''8 cores em cada através das IRQs'''. Usamos o parâmetro '''-l''' do '''ethtool''' para listar o '''Pre-set maximums combined''' da interface e o parâmetro '''-L''' para alterar esse valor. Façamos então a alteração:<br />
# ethtool -L enp5s0f0 combined 8<br />
# ethtool -L enp5s0f1 combined 8<br />
# ethtool -L enp6s0f0 combined 8<br />
# ethtool -L enp6s0f1 combined 8<br />
Com os comandos acima deixamos preparadas as interfaces para aceitarem '''8 cores em cada uma através das IRQs'''. <br />
<br />
Não podemos usar o programa '''irqbalance''' para o '''CPU Affinity''', pois este faz migração de contextos entre os cores e isso é ruim. Como no nosso exemplo estamos usando uma interface Intel, utilizaremos um script da própria Intel para realizar o '''CPU Affinity''' de forma mais fácil. Esse script se chama '''set_irq_affinity''' e vem acompanhado com os fontes do driver da interface. Ex.: '''[https://www.intel.com/content/www/us/en/download/18026/intel-network-adapter-driver-for-pcie-40-gigabit-ethernet-network-connections-under-linux.html Intel Network Adapter]'''<br />
<br />
=== Código do script '''set_irq_affinity''' ===<br />
#!/bin/bash<br />
# SPDX-License-Identifier: BSD-3-Clause<br />
# Copyright (c) 2015 - 2019, Intel Corporation<br />
#<br />
# Affinitize interrupts to cores<br />
#<br />
# typical usage is (as root):<br />
# set_irq_affinity -x local eth1 <eth2> <eth3><br />
# set_irq_affinity -s eth1<br />
#<br />
# to get help:<br />
# set_irq_affinity<br />
<br />
usage()<br />
{<br />
echo<br />
echo "Usage: option -s <interface> to show current settings only"<br />
echo "Usage: $0 [-x|-X] [all|local|remote [<node>]|one <core>|custom|<cores>] <interface> ..."<br />
echo " Options: "<br />
echo " -s Shows current affinity settings"<br />
echo " -x Configure XPS as well as smp_affinity"<br />
echo " -X Disable XPS but set smp_affinity"<br />
echo " [all] is the default value"<br />
echo " [remote [<node>]] can be followed by a specific node number"<br />
echo " Examples:"<br />
echo " $0 -s eth1 # Show settings on eth1"<br />
<br />
echo " $0 all eth1 eth2 # eth1 and eth2 to all cores"<br />
echo " $0 one 2 eth1 # eth1 to core 2 only"<br />
echo " $0 local eth1 # eth1 to local cores only"<br />
echo " $0 remote eth1 # eth1 to remote cores only"<br />
echo " $0 custom eth1 # prompt for eth1 interface"<br />
echo " $0 0-7,16-23 eth0 # eth1 to cores 0-7 and 16-23"<br />
echo<br />
exit 1<br />
}<br />
<br />
usageX()<br />
{<br />
echo "options -x and -X cannot both be specified, pick one"<br />
exit 1<br />
}<br />
<br />
if [ "$1" == "-x" ]; then<br />
XPS_ENA=1<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-s" ]; then<br />
SHOW=1<br />
echo Show affinity settings<br />
shift<br />
fi<br />
<br />
if [ "$1" == "-X" ]; then<br />
if [ -n "$XPS_ENA" ]; then<br />
usageX<br />
fi<br />
XPS_DIS=2<br />
shift<br />
fi<br />
<br />
if [ "$1" == -x ]; then<br />
usageX<br />
fi<br />
<br />
if [ -n "$XPS_ENA" ] && [ -n "$XPS_DIS" ]; then<br />
usageX<br />
fi<br />
<br />
if [ -z "$XPS_ENA" ]; then<br />
XPS_ENA=$XPS_DIS<br />
fi<br />
<br />
SED=`which sed`<br />
if <nowiki>[[ ! -x $SED ]]</nowiki>; then<br />
echo " $0: ERROR: sed not found in path, this script requires sed"<br />
exit 1<br />
fi<br />
<br />
num='^[0-9]+$'<br />
<br />
# search helpers<br />
NOZEROCOMMA="s/^[0,]*//"<br />
# Vars<br />
AFF=$1<br />
shift<br />
<br />
case "$AFF" in<br />
remote) <nowiki>[[ $1 =~ $num ]]</nowiki> && rnode=$1 && shift ;;<br />
one) <nowiki>[[ $1 =~ $num ]]</nowiki> && cnt=$1 && shift ;;<br />
all) ;;<br />
local) ;;<br />
custom) ;;<br />
[0-9]*) ;;<br />
-h|--help) usage ;;<br />
"") usage ;;<br />
*) IFACES=$AFF && AFF=all ;; # Backwards compat mode<br />
esac<br />
<br />
# append the interfaces listed to the string with spaces<br />
while [ "$#" -ne "0" ] ; do<br />
IFACES+=" $1"<br />
shift<br />
done<br />
<br />
# for now the user must specify interfaces<br />
if [ -z "$IFACES" ]; then<br />
usage<br />
exit 2<br />
fi<br />
<br />
notfound()<br />
{<br />
echo $MYIFACE: not found<br />
exit 15<br />
}<br />
<br />
# check the interfaces exist<br />
for MYIFACE in $IFACES; do<br />
grep -q $MYIFACE /proc/net/dev || notfound<br />
done<br />
<br />
# support functions<br />
<br />
build_mask()<br />
{<br />
VEC=$core<br />
if [ $VEC -ge 32 ]<br />
then<br />
MASK_FILL=""<br />
MASK_ZERO="00000000"<br />
let "IDX = $VEC / 32"<br />
for ((i=1; i<=$IDX;i++))<br />
do<br />
MASK_FILL="${MASK_FILL},${MASK_ZERO}"<br />
done<br />
<br />
let "VEC -= 32 * $IDX"<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X%s" $MASK_TMP $MASK_FILL)<br />
else<br />
MASK_TMP=$((1<<$VEC))<br />
MASK=$(printf "%X" $MASK_TMP)<br />
fi<br />
}<br />
<br />
show_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
HINT=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/affinity_hint`<br />
printf "ACTUAL %s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf "HINT %s %d %s <- /proc/irq/$IRQ/affinity_hint\n" $IFACE $core $HINT<br />
IRQ_CHECK=`grep '[-,]' /proc/irq/$IRQ/smp_affinity_list`<br />
if [ ! -z $IRQ_CHECK ]; then<br />
printf " WARNING -- SMP_AFFINITY is assigned to multiple cores $IRQ_CHECK\n"<br />
fi<br />
if [ "$SMP_I" != "$HINT" ]; then<br />
printf " WARNING -- SMP_AFFINITY VALUE does not match AFFINITY_HINT \n"<br />
fi<br />
printf "NODE %s %d %s <- /proc/irq/$IRQ/node\n" $IFACE $core `cat /proc/irq/$IRQ/node`<br />
printf "LIST %s %d [%s] <- /proc/irq/$IRQ/smp_affinity_list\n" $IFACE $core `cat /proc/irq/$IRQ/smp_affinity_list`<br />
printf "XPS %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` ]; then<br />
echo "WARNING: xps rxqs not supported on $IFACE"<br />
else<br />
printf "XPSRXQs %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_rxqs\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` $IFACE $((n-1))<br />
fi<br />
printf "TX_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/tx_maxrate\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/tx_maxrate` $IFACE $((n-1))<br />
printf "BQLIMIT %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit` $IFACE $((n-1))<br />
printf "BQL_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_max\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_max` $IFACE $((n-1))<br />
printf "BQL_MIN %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_min\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_min` $IFACE $((n-1))<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` ]; then<br />
echo "WARNING: aRFS is not supported on $IFACE"<br />
else<br />
printf "RPSFCNT %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_flow_cnt\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` $IFACE $((n-1))<br />
fi<br />
if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` ]; then<br />
echo "WARNING: rps_cpus is not available on $IFACE"<br />
else<br />
printf "RPSCPU %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` $IFACE $((n-1))<br />
fi<br />
echo<br />
}<br />
<br />
set_affinity()<br />
{<br />
# returns the MASK variable<br />
build_mask<br />
<br />
printf "%s" $MASK > /proc/irq/$IRQ/smp_affinity<br />
printf "%s %d %s -> /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $MASK<br />
SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity`<br />
if [ "$SMP_I" != "$MASK" ]; then<br />
printf " ACTUAL\t%s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I<br />
printf " WARNING -- SMP_AFFINITY setting failed\n"<br />
fi<br />
case "$XPS_ENA" in<br />
1)<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
2)<br />
MASK=0<br />
printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1))<br />
printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus<br />
;;<br />
*)<br />
esac<br />
}<br />
<br />
# Allow usage of , or -<br />
#<br />
parse_range () {<br />
RANGE=${@//,/ }<br />
RANGE=${RANGE//-/..}<br />
LIST=""<br />
for r in $RANGE; do<br />
# eval lets us use vars in {#..#} range<br />
<nowiki>[[ $r =~ '..' ]]</nowiki> && r="$(eval echo {$r})"<br />
LIST+=" $r"<br />
done<br />
echo $LIST<br />
}<br />
<br />
# Affinitize interrupts<br />
#<br />
doaff()<br />
{<br />
CORES=$(parse_range $CORES)<br />
ncores=$(echo $CORES | wc -w)<br />
n=1<br />
<br />
# this script only supports interrupt vectors in pairs,<br />
# modification would be required to support a single Tx or Rx queue<br />
# per interrupt vector<br />
<br />
queues="${IFACE}-.*TxRx"<br />
<br />
irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:)<br />
[ -z "$irqs" ] && irqs=$(for i in `ls -1 /sys/class/net/${IFACE}/device/msi_irqs | sort -n` ;do grep -w $i: /proc/interrupts | egrep -v 'fdir|async|misc|ctrl' | cut -f 1 -d :; done)<br />
[ -z "$irqs" ] && echo "Error: Could not find interrupts for $IFACE"<br />
<br />
if [ "$SHOW" == "1" ] ; then<br />
echo "TYPE IFACE CORE MASK -> FILE"<br />
echo "============================"<br />
else<br />
echo "IFACE CORE MASK -> FILE"<br />
echo "======================="<br />
fi<br />
<br />
for IRQ in $irqs; do<br />
[ "$n" -gt "$ncores" ] && n=1<br />
j=1<br />
# much faster than calling cut for each<br />
for i in $CORES; do<br />
[ $((j++)) -ge $n ] && break<br />
done<br />
core=$i<br />
if [ "$SHOW" == "1" ] ; then<br />
show_affinity<br />
else<br />
set_affinity<br />
fi<br />
((n++))<br />
done<br />
}<br />
<br />
# these next 2 lines would allow script to auto-determine interfaces<br />
#[ -z "$IFACES" ] && IFACES=$(ls /sys/class/net)<br />
#[ -z "$IFACES" ] && echo "Error: No interfaces up" && exit 1<br />
<br />
# echo IFACES is $IFACES<br />
<br />
CORES=$(</sys/devices/system/cpu/online)<br />
[ "$CORES" ] || CORES=$(grep ^proc /proc/cpuinfo | cut -f2 -d:)<br />
<br />
# Core list for each node from sysfs<br />
node_dir=/sys/devices/system/node<br />
for i in $(ls -d $node_dir/node*); do<br />
i=${i/*node/}<br />
corelist[$i]=$(<$node_dir/node${i}/cpulist)<br />
done<br />
<br />
for IFACE in $IFACES; do<br />
# echo $IFACE being modified<br />
<br />
dev_dir=/sys/class/net/$IFACE/device<br />
[ -e $dev_dir/numa_node ] && node=$(<$dev_dir/numa_node)<br />
[ "$node" ] && [ "$node" -gt 0 ] || node=0<br />
<br />
case "$AFF" in<br />
local)<br />
CORES=${corelist[$node]}<br />
;;<br />
remote)<br />
[ "$rnode" ] || { [ $node -eq 0 ] && rnode=1 || rnode=0; }<br />
CORES=${corelist[$rnode]}<br />
;;<br />
one)<br />
[ -n "$cnt" ] || cnt=0<br />
CORES=$cnt<br />
;;<br />
all)<br />
CORES=$CORES<br />
;;<br />
custom)<br />
echo -n "Input cores for $IFACE (ex. 0-7,15-23): "<br />
read CORES<br />
;;<br />
[0-9]*)<br />
CORES=$AFF<br />
;;<br />
*)<br />
usage<br />
exit 1<br />
;;<br />
esac<br />
<br />
# call the worker function<br />
doaff<br />
done<br />
<br />
# check for irqbalance running<br />
IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?`<br />
if [ "$IRQBALANCE_ON" == "0" ] ; then<br />
echo " WARNING: irqbalance is running and will"<br />
echo " likely override this script's affinitization."<br />
echo " Please stop the irqbalance service and/or execute"<br />
echo " 'killall irqbalance'"<br />
exit 2<br />
fi<br />
<br />
=== CPU Affinity ===<br />
Agora que preparamos as interfaces, façamos os apontamentos dos cores da seguinte forma. Vamos supor que colocamos o script em '''/root/scripts''':<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
# /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
# /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
<br />
=== Mais alguns tunings ===<br />
Vamos fazer mais alguns ajustes nas interfaces com o '''ethtool'''. Dessa vez vamos aumentar os '''Rings RX''' e '''TX'''. Mas antes vamos listar os valores que podemos usar:<br />
# ethtool -g enp5s0f0<br />
Ring parameters for enp5s0f0:<br />
Pre-set maximums:<br />
RX: 4096<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 4096<br />
Current hardware settings:<br />
RX: 512<br />
RX Mini: n/a<br />
RX Jumbo: n/a<br />
TX: 512<br />
Acima vemos que o '''valor máximo é de 4096 tanto para TX''', '''quanto para RX''' mas está '''configurado para 512 em RX e TX'''. Façamos então:<br />
# ethtool -G enp5s0f0 rx 4096 tx 4096<br />
# ethtool -G enp5s0f1 rx 4096 tx 4096<br />
# ethtool -G enp6s0f0 rx 4096 tx 4096<br />
# ethtool -G enp6s0f1 rx 4096 tx 4096<br />
Vamos desabilitar as seguintes options das interfaces: '''TSO''', '''GRO''' e '''GSO'''. <br />
# ethtool -K enp5s0f0 tso off gro off gso off<br />
# ethtool -K enp5s0f1 tso off gro off gso off<br />
# ethtool -K enp6s0f0 tso off gro off gso off<br />
# ethtool -K enp6s0f1 tso off gro off gso off<br />
Aumentaremos o '''txqueuelen''' para '''10000''':<br />
# ip link set enp5s0f0 txqueuelen 10000<br />
# ip link set enp5s0f1 txqueuelen 10000<br />
# ip link set enp6s0f0 txqueuelen 10000<br />
# ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Salvando a configuração e criando o LACP ===<br />
Tudo que fizemos até o momento será perdido no próximo reboot do sistema, então faremos com que esses comandos sejam executados sempre que o sistema iniciar. Para isso vamos deixar o nosso arquivo '''/etc/network/interfaces''' configurado conforme nosso diagrama, usando '''LACP''' e executando nossos comandos anteriores.<br />
<br />
Antes precisaremos instalar o pacote '''ifenslave''' para que o bonding funcione:<br />
# apt install ifenslave<br />
# modprobe bonding<br />
# echo "bonding" >> /etc/modules<br />
Abaixo o nosso '''/etc/network/interfaces''' já com todas as configurações que fizemos anteriormente e seguindo nosso diagrama de exemplo:<br />
# This file describes the network interfaces available on your system<br />
# and how to activate them. For more information, see interfaces(5).<br />
<br />
source /etc/network/interfaces.d/*<br />
<br />
# The loopback network interface<br />
auto lo<br />
iface lo inet loopback<br />
<br />
auto bond0<br />
iface bond0 inet static<br />
bond-slaves enp5s0f0 enp5s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 10.0.10.172/24<br />
gateway 10.0.10.1<br />
pre-up /usr/sbin/ethtool -L enp5s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp5s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f0<br />
pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f1<br />
pre-up /usr/sbin/ethtool -G enp5s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp5s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp5s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp5s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp5s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp5s0f1 txqueuelen 10000<br />
<br />
auto bond1<br />
iface bond1 inet static<br />
bond-slaves enp6s0f0 enp6s0f1<br />
bond_mode 802.3ad<br />
bond-ad_select bandwidth<br />
bond_miimon 100<br />
bond_downdelay 200<br />
bond_updelay 200<br />
bond-lacp-rate 1<br />
bond-xmit-hash-policy layer2+3<br />
address 192.168.0.1/24<br />
pre-up /usr/sbin/ethtool -L enp6s0f0 combined 8<br />
pre-up /usr/sbin/ethtool -L enp6s0f1 combined 8<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f0<br />
pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f1<br />
pre-up /usr/sbin/ethtool -G enp6s0f0 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -G enp6s0f1 rx 4096 tx 4096<br />
pre-up /usr/sbin/ethtool -K enp6s0f0 tso off gro off gso off<br />
pre-up /usr/sbin/ethtool -K enp6s0f1 tso off gro off gso off<br />
pre-up /usr/sbin/ip link set enp6s0f0 txqueuelen 10000<br />
pre-up /usr/sbin/ip link set enp6s0f1 txqueuelen 10000<br />
<br />
=== Atualizando o Kernel ===<br />
Colocaremos o '''kernel do backports'''. Para isso deixe o seu '''/etc/apt/sources''' conforme abaixo e rode os comandos na sequência:<br />
deb <nowiki>http://security.debian.org/debian-security</nowiki> bullseye-security main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye main non-free contrib<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-updates main contrib non-free<br />
deb <nowiki>http://deb.debian.org/debian</nowiki> bullseye-backports main contrib non-free<br />
<br />
# apt update<br />
# apt install -t bullseye-backports linux-image-amd64<br />
# reboot<br />
<br />
=== Protegendo contra static loop e preparando o ambiente do CGNAT ===<br />
O '''static loop''' é algo que, definitivamente, pode derrubar toda a sua operação se não for devidamente tratado e pode ser facilmente explorado por pessoas mal intencionadas. A causa do problema é uma rota estática para um prefixo IP (seja IPv4 ou IPv6), que aponta para um next-hop e nesse destino não existe nenhuma informação sobre o prefixo IP na tabela de rotas local, obrigando o pacote a retornar para o seu gateway default e ficando nesse loop até que '''expire o TTL (Time To Live) do pacote'''. Isso ocorre muito nos casos em que temos concentradores PPPoE (BNG) e caixas CGNAT como esta que estaremos fazendo. Em '''[[Recomendações sobre Mitigação DDoS]]''' temos outras dicas de segurança sobre o assunto '''DDoS'''.<br />
<br />
Crie um arquivo /etc/rc.local e dentro colocaremos algumas coisas como as blackholes para cada prefixo IPv4 público que usaremos no nosso servidor de exemplo e rotas de retorno para o nosso BNG:<br />
# > /etc/rc.local<br />
# chmod +x /etc/rc.local<br />
Dentro teremos:<br />
#!/bin/sh -e<br />
/usr/sbin/ip route add blackhole 198.18.0.0/27 metric 254<br />
/usr/sbin/ip route add 100.64.0.0/22 via 192.168.0.2<br />
No exemplo acima estamos colocando em '''blackhole''' o nosso prefixo IPv4 público deste tutorial que é o '''198.18.0.0/27''' e adicionando uma rota de retorno do prefixo '''100.64.0.0/22''' usado no nosso '''BNG''' para o '''next-hop 192.168.0.2'''.<br />
<br />
=== Redução dos tempos de timeouts e outros ajustes ===<br />
Os tempos padrões dos '''timeouts''' de '''tcp''' e '''udp''' são altos para o nosso sistema de CGNAT, ainda mais quando estamos '''diminuindo a quantidade de portas tcp/udp por assinante''' e com isso podemos rapidamente estourar esse limite, fazendo com que o sistema pare de funcionar. Abaixo estou colocando os valores que sempre usei e não percebi problemas, mas você pode ajustar conforme achar mais prudente. Adicionaremos as configurações abaixo também no nosso '''/etc/rc.local''': <br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_sent<br />
echo 5 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_recv<br />
echo 86400 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_fin_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_last_ack<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_max_retrans<br />
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_unacknowledged<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout<br />
echo 180 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream<br />
echo 10 > /proc/sys/net/netfilter/nf_conntrack_icmp_timeout<br />
echo 600 > /proc/sys/net/netfilter/nf_conntrack_generic_timeout<br />
Em '''/etc/sysctl.conf''' adicionaremos:<br />
net.core.default_qdisc=fq<br />
net.ipv4.tcp_congestion_control=bbr<br />
net.core.rmem_max = 2147483647<br />
net.core.wmem_max = 2147483647<br />
net.ipv4.tcp_rmem = 4096 87380 2147483647<br />
net.ipv4.tcp_wmem = 4096 65536 2147483647<br />
net.ipv4.conf.all.forwarding=1<br />
net.netfilter.nf_conntrack_helper=1<br />
net.netfilter.nf_conntrack_buckets = 512000<br />
net.netfilter.nf_conntrack_max = 4096000<br />
vm.swappiness=10<br />
As configurações acima melhoram o uso de memória, habilita o encaminhamento dos pacotes e aumenta a quantidade máxima de '''conntracks''' do sistema para '''4096000'''.<br />
<br />
'''Se o conntrack estourar, seu CGNAT terá problemas e causará indisponibilidades'''. Para consultar a quantidade de conntracks em uso:<br />
# cat /proc/sys/net/netfilter/nf_conntrack_count<br />
Para listar as '''conntracks''':<br />
# cat /proc/net/nf_conntrack<br />
<br />
=== Ajustando a data e horário do sistema ===<br />
Uma tarefa muito importante a ser feita nos servidores, é garantir que o horário e data estejam corretos e para isso usaremos o programa '''chrony'''. Eu prefiro usar sempre horário UTC nos servidores e fazer a conversão quando necessário:<br />
# apt install chrony<br />
Basta copiar e colar os comandos abaixo, para configurar o '''chrony''':<br />
# cat << EOF > /etc/chrony/chrony.conf<br />
confdir /etc/chrony/conf.d<br />
sourcedir /run/chrony-dhcp<br />
sourcedir /etc/chrony/sources.d<br />
keyfile /etc/chrony/chrony.keys<br />
driftfile /var/lib/chrony/chrony.drift<br />
ntsdumpdir /var/lib/chrony<br />
logdir /var/log/chrony<br />
maxupdateskew 100.0<br />
rtcsync<br />
makestep 1 3<br />
leapsectz right/UTC<br />
EOF<br />
<br />
# cat << EOF > /etc/chrony/sources.d/nic.sources<br />
server a.st1.ntp.br iburst nts<br />
server b.st1.ntp.br iburst nts<br />
server c.st1.ntp.br iburst nts<br />
server d.st1.ntp.br iburst nts<br />
EOF<br />
Aqui reiniciamos o serviço e configuramos o '''timezone''':<br />
# systemctl restart chronyd.service<br />
# timedatectl set-timezone "UTC"<br />
<br />
=== Habilitando ALG (Application Layer Gateway) ===<br />
No arquivo '''/etc/modules''' adicionaremos os módulos que usaremos no nosso CGNAT, inclusive os ALGs'''.''' Sem eles alguns serviços, ainda muito utilizados, apresentarão problemas.<br />
<br />
Em '''/etc/modules''' adicionaremos mais os módulos abaixo:<br />
nf_conntrack<br />
nf_nat_pptp<br />
nf_nat_h323<br />
nf_nat_sip<br />
nf_nat_irc<br />
nf_nat_ftp<br />
nf_nat_tftp<br />
<br />
=== Preparando ambiente e gerador de regras de CGNAT ===<br />
Antes de começarmos nossas regras de CGNAT precisaremos de alguns pacotes:<br />
# apt install python3-pip nftables<br />
# pip install ipaddress<br />
Vamos precisar também de um gerador de regras de CGNAT para '''nftables'''. Porque criar as regras manualmente não é uma tarefa rápida e para isso usaremos um programa em python criado por '''José Beiriz''' e disponibilizado aqui: '''[https://github.com/Beiriz/GRCN GRCN]'''<br />
<br />
Caso não consigam baixar por algum motivo o '''GRCN''', aqui '''<nowiki>https://github.com/gondimcodes/GRCN</nowiki>''' também pode ser encontrado o script '''cgnat-nft.py.'''<br />
<br />
Nosso sistema de regras CGNAT será dividido em 2 partes:<br />
<br />
* O script base que colocaremos em '''/root/scripts''' chamado de '''frw-nft.sh'''. Esse script conterá as regras básicas do CGNAT e este incluirá a chamada para os outros arquivos de regras propriamente ditos do CGNAT. <br />
<br />
* Essa outra parte é composta pelos arquivos de regras de CGNAT, onde são feitas as traduções de IPs privados '''100.64.0.0/10 (Shared Address Space - RFC6598)''', para os '''IPs públicos'''. A seguir o '''frw-nft.sh''':<br />
<br />
Nosso script de CGNAT base '''/root/scripts/frw-nft.sh''':<br />
#!/usr/sbin/nft -f<br />
# limpa todas as regras da memoria<br />
flush ruleset<br />
<br />
# regras base para o CGNAT<br />
add table ip nat<br />
add chain ip nat POSTROUTING { type nat hook postrouting priority 100; policy accept; }<br />
<br />
add chain ip nat CGNATOUT<br />
<br />
# libera o proprio CGNAT para acessar a Internet - para atualizacoes por exemplo<br />
add rule ip nat POSTROUTING oifname "bond0" ip saddr 10.0.10.172 counter snat to 198.18.0.0<br />
<br />
# faz o jump para as regras de CGNAT<br />
add rule ip nat POSTROUTING oifname "bond0" counter jump CGNATOUT<br />
<br />
'''# carrega os arquivos de regras de CGNAT'''<br />
include "/root/scripts/cgnat-0-31.conf"<br />
A última linha do script acima, em '''negrito''', é o arquivo de regras CGNAT que iremos gerar e será chamado pelo script quando for executado.<br />
<br />
Após a criação do script, alteramos a permissão dele para ficar como executável e adicionamos ele em nosso '''/etc/rc.local''':<br />
# chmod 700 /root/scripts/frw-nft.sh<br />
# echo "/root/scripts/frw-nft.sh" >> /etc/rc.local<br />
<br />
=== Gerando nossas regras de CGNAT ===<br />
Colocaremos o script '''cgnat-nft.py''' em '''/root/scripts/'''. Como estamos trabalhando '''no modelo determinístico de 1/32''', basta pegarmos nosso bloco privado '''100.64.0.0/22 (1024 IPs)''' e nosso bloco público '''198.18.0.0/27 (32 IPs)''' e executarmos em linha de comando:<br />
# cd /root/scripts<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
Se digitar apenas '''./cgnat-nft.py''' será apresentado um help dos parâmetros mas é bem simples o seu uso. No comando acima '''temos o número 0 como índice'''. Muito cuidado com o índice, porque ele é muito importante para a performance e para cada novo arquivo gerado, esse índice precisará ser incrementado. O comando acima criará automaticamente o arquivo chamado '''cgnat-0-31.conf''', aquele mesmo visto no script base sendo carregado com o '''include'''. Onde esse '''0-31''' quer dizer que nesse arquivo '''os índices vão de 0 a 31'''. Se for gerar um novo arquivo com o comando acima, o próximo índice a ser usado seria o '''32'''. Por exemplo:<br />
# ./cgnat-nft.py '''32''' 198.18.0.32/27 100.64.4.0/22 1/32<br />
Esse comando acima criará novas regras no arquivo chamado '''cgnat-32-63.conf''', na sequência inclua esse novo arquivo dentro do '''/root/scripts/frw-nft.sh''' e '''execute o /root/scripts/frw-nft.sh novamente''' para carregar as novas regras. A seguir daremos uma olhada nas regras geradas nesses arquivos.<br />
<br />
=== Executando o gerador de regras ===<br />
# ./cgnat-nft.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
[[Arquivo:1022px-Grcn.png|nenhum|miniaturadaimagem|1022x1022px]]<br />
Após teclar '''ENTER''' será gerado o arquivo '''cgnat-0-31.conf''' com as regras conforme a tela abaixo de exemplo:<br />
[[Arquivo:1027px-Regras01.png|nenhum|miniaturadaimagem|1027x1027px]]<br />
Na tela abaixo se observarmos o '''retângulo vermelho''' veremos a regra que faz o '''NAT de tudo que não for TCP ou UDP''' e por fim a regra que faz o '''jump''' '''de tudo que for origem 100.64.0.0/27''' para o '''CGNATOUT_0''' onde esse '''0 é o índice'''.<br />
[[Arquivo:1029px-Regras02.png|nenhum|miniaturadaimagem|1029x1029px]]<br />
<br />
=== Explicando a função dos índices ===<br />
O sistema de avaliação de regras de filtros de pacotes e NAT no GNU/Linux é do tipo '''First Match Win''', o que significa que a pesquisa das regras se encerra quando o sistema encontra uma regra que dê match. O sistema fica muito mais otimizado e performático quando quebramos as regras e separamos em '''CHAINS''' e é aí que entram os '''índices'''. Porque as '''CHAINS''' não podem ter o mesmo nome, senão não haveria separação das regras. A seguir veremos por exemplo que quando houver um pacote relacionado com o prefixo de origem '''100.64.0.0/27''', este será encaminhado para a chain '''CGNATOUT_0''', que é onde estão as regras de CGNAT para esse bloco IP. Desse jeito a checagem para esse prefixo não percorre todas as regras de NAT contidas na memória.<br />
[[Arquivo:Regras03.png|nenhum|miniaturadaimagem|1034x1034px]]<br />
<br />
=== Novo script gerador de regras nftables com suporte a netmap ===<br />
Com a versão nova que virá do '''Debian''', a '''versão 12 (Bookworm)''', teremos também uma versão nova do '''nftables''' '''1.0.6''' e essa versão já suporta o equivalente ao '''netmap''' que temos no '''Mikrotik''' e com isso teremos menos regras na memória e provavelmente mais performance. O sistema novo conta também com o '''kernel 6.1.27''' que possui diversas '''melhorias na pilha tcp/ip'''. Para aqueles que já quiserem testar nesse novo ambiente, fiz uma modificação no script python mostrado anteriormente, para gerar regras nesse novo formato e um arquivo tabela com o relacionamento de portas e IPs para quebra de sigilo tecnológico. Abaixo o novo código e estarei solicitando ao '''José Beiriz''' para incorporá-lo no '''GRCN'''.<br />
#!/usr/bin/env python3<br />
<nowiki>#</nowiki> -*- coding: utf-8 -*-<br />
<nowiki>'''</nowiki># -*- coding: latin-1 -*-<nowiki>'''</nowiki><br />
<br />
<nowiki>'''</nowiki><br />
GRCN is free software; you can redistribute it and/or modify<br />
it under the terms of the GNU General Public License as published by<br />
the Free Software Foundation; either version 2 of the License, or<br />
(at your option) any later version.<br />
<nowiki>#</nowiki><br />
This program is distributed in the hope that it will be useful,<br />
but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
GNU General Public License for more details.<br />
<nowiki>#</nowiki><br />
You should have received a copy of the GNU General Public License<br />
along with this program; if not, write to the Free Software<br />
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
<nowiki>'''</nowiki><br />
<br />
import os<br />
import sys<br />
import time<br />
import ipaddress<br />
<br />
__author__ = 'Beiriz & Gondim'<br />
__version__= 1.000<br />
__datebegin__= "27/07/2020 (28/05/2023)"<br />
__com1__ = "add rule ip nat"<br />
<br />
<nowiki>#</nowiki>-----------------------------------------------------------------------<br />
fazer_regras_in = False #Este valor deve ser alterado para True caso haja interesse de gerar também as regras de CGNAT no sentido IN: 'fazer_regras_in = True'. OBS: CGNAT do tipo OUT sempre serão geradas.<br />
indice = 0<br />
txt_publico = ""<br />
txt_privado = ""<br />
qt_ips_publicos = 0 #Quantidade de IPs públicos na rede informada<br />
qt_ips_privados = 0 #Quantidade de IPs privados na rede informada<br />
qt_ips_privados_por_ip_publico = 0 #Quantos IPs privados vão sair por um único IP público ( A relação PRI/PUB)<br />
qt_portas_por_ip = 2016 #quantidade de portas que serão reservadas por IP privado.<br />
numero_porta_incial = 1024<br />
numero_porta_final = 65535<br />
relacao = '1/32'<br />
qt_total_portas = (numero_porta_final-(numero_porta_incial-1))<br />
<nowiki>#</nowiki>As 2 confs abaixo trabalham em conjunto para ajustar o range total de portas de cada IP público<br />
relacao_portas = {'1/4':16128, '1/8':8064, '1/16':4032, '1/32':2016, '1/64':1008, '1/128':504, '1/256':252}<br />
relacao_mascara = {'1/4':30, '1/8':29, '1/16':28, '1/32':27, '1/64':26, '1/128':25, '1/256':24}<br />
relacao_ips_masq = {'1/4':4, '1/8':8, '1/16':16, '1/32':32, '1/64':64, '1/128':128, '1/256':256}<br />
<nowiki>#</nowiki>-------------------------------------------------<br />
<br />
os.system('cls' if os.name == 'nt' else 'clear')<br />
titulo = "GRCN - Gerador de Regras CGNAT em nftables (NETMAP) - %s - v%s - %s" % (__author__, __version__,__datebegin__)<br />
print("#"*106)<br />
print(" %s" %(titulo))<br />
print("#"*106)<br />
<br />
<nowiki>#</nowiki>------------------------------------------------- Parâmetros informados / manual:<br />
<br />
try:<br />
<nowiki> </nowiki> #Indice:<br />
<nowiki> </nowiki> indice = int(sys.argv[1])<br />
<nowiki> </nowiki> #Blocos:<br />
<nowiki> </nowiki> txt_publico = str(sys.argv[2])<br />
<nowiki> </nowiki> txt_privado = str(sys.argv[3])<br />
<nowiki> </nowiki> #RELAÇÃO_IP_PUBLICO_X_CLIENTE<br />
<nowiki> </nowiki> try:<br />
<nowiki> </nowiki> relacao = str(sys.argv[4])<br />
<nowiki> </nowiki> except:<br />
<nowiki> </nowiki> relacao = '1/32'<br />
<nowiki> </nowiki> finally:<br />
<nowiki> </nowiki> qt_portas_por_ip = relacao_portas[relacao]<br />
<nowiki> </nowiki> print("\n\t[ Ídice inicial: %i | público: %s | privado: %s | %i portas/IP (%s)]\t\n" % (indice, txt_publico, txt_privado, qt_portas_por_ip, relacao))<br />
<br />
except:<br />
<nowiki> </nowiki> print("\nErro! Informe pelo menos os parâmetros obrigatórios deste script.\n")<br />
<nowiki> </nowiki> print("## Manual de Instruções:")<br />
<nowiki> </nowiki> print("\n###### Exemplo básico (1/32):\n")<br />
<nowiki> </nowiki> print("```")<br />
<nowiki> </nowiki> print("%spython %s <INDICE> <BLOCO_PUBLICO> <BLOCO_PRIVADO>" %(' '*6, sys.argv[0]))<br />
<nowiki> </nowiki> print("%spython %s 0 192.0.2.0/24 100.69.0.0/22" %(' '*6, sys.argv[0]))<br />
<nowiki> </nowiki> print("```")<br />
<nowiki> </nowiki> print("\n###### Exemplo avançado:\n")<br />
<nowiki> </nowiki> print("```")<br />
<nowiki> </nowiki> print("%s python %s <INDICE> <BLOCO_PUBLICO> <BLOCO_PRIVADO> <RELAÇÃO_IP_PUBLICO_X_CLIENTE>(OPCIONAL)" %(' '*6, sys.argv[0]))<br />
<nowiki> </nowiki> print("%s python %s 0 192.0.2.0/24 100.69.0.0/22 1/16")<br />
<nowiki> </nowiki> print("```")<br />
<nowiki> </nowiki> print("\n###### Parâmetros:\n")<br />
<nowiki> </nowiki> print("* INDICE: Inteiro >=0 que vai ser o sufixo do nome das regras únicas. Exemplo *CGNATOUT_XXX*;\n")<br />
<nowiki> </nowiki> print("* BLOCO_PUBLICO: É o bloco de IPs públicos por onde o bloco CGNAT vai sair para a internet. Exemplo: *192.0.2.0/24*\n")<br />
<nowiki> </nowiki> print("* BLOCO_PRIVADO: É o bloco de IPs privados que serão entregues ao assinante.\n")<br />
<nowiki> </nowiki> print("* RELAÇÃO_IP_PUBLICO_X_CLIENTE (OPCIONAL):")<br />
<nowiki> </nowiki> print(" - 1/4 - 16128 portas por IP;")<br />
<nowiki> </nowiki> print(" - 1/8 - 8064 portas por IP;")<br />
<nowiki> </nowiki> print(" - 1/16 - 4032 portas por IP;")<br />
<nowiki> </nowiki> print(" - 1/32 - 2016 portas por IP (Configuração padrão, quando este último parâmetro não é informado);")<br />
<nowiki> </nowiki> print(" - 1/64 - 1008 portas por IP (Atenção! Não recomendado pelas boas práticas);")<br />
<nowiki> </nowiki> print(" - 1/128 - 504 portas por IP (Atenção! Não recomendado pelas boas práticas);")<br />
<nowiki> </nowiki> print(" - 1/256 - 252 portas por IP (Atenção! Não recomendado pelas boas práticas);")<br />
<nowiki> </nowiki> print("\n####### Observações:\n")<br />
<nowiki> </nowiki> print("* Este script vai dividir o <BLOCO_PRIVADO> em N sub-redes privadas. Cada sub-rede privada sai por um único IP público e dela, cada IP privado sai com uma fração das portas de seu IP público.\n")<br />
<nowiki> </nowiki> print("* Se <BLOCO_PUBLICO> for um /24 e <BLOCO_PRIVADO> um /19 e a relação for 1/32, serão colocados exatamente 32 IPs privados (assinantes) atrás de um IP público. Cada IP privado vai sair com 2016 portas de seu IP público (65535-1023)/32. O famoso *1:32*.\n")<br />
<nowiki> </nowiki> print("\n")<br />
<nowiki> </nowiki> print("\nATENÇÃO! Por boas práticas, o script PAROU de gerar as regras CGNAT do tipo IN. Caso queira continuar gerando-as, edite o cgnat-nft.py, alterando o valor *fazer_regras_in* de *False* para *True*;")<br />
<nowiki> </nowiki> print("\nFIM deste manual!\n")<br />
<nowiki> </nowiki> exit(0)<br />
<nowiki>#</nowiki>------------------------------------------------- trata os parâmetros informados:<br />
<br />
try:<br />
<nowiki> </nowiki> if sys.version_info >= (3,0):<br />
<nowiki> </nowiki> rede_publica = ipaddress.ip_network(str(txt_publico), strict=False)<br />
<nowiki> </nowiki> rede_privada = ipaddress.ip_network(str(txt_privado), strict=False)<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> rede_publica = ipaddress.ip_network(unicode(txt_publico), strict=False)<br />
<nowiki> </nowiki> rede_privada = ipaddress.ip_network(unicode(txt_privado), strict=False)<br />
<nowiki> </nowiki> qt_ips_publicos = int(rede_publica.num_addresses)<br />
<nowiki> </nowiki> qt_ips_privados = int(rede_privada.num_addresses)<br />
<nowiki> </nowiki> qt_ips_privados_por_ip_publico = int( qt_ips_privados / qt_ips_publicos )<br />
<nowiki> </nowiki> # Nome arquivo de destino<br />
<nowiki> </nowiki> nome_arquivo_regras = ("cgnat-%i-%i.conf" % (indice,(indice + qt_ips_publicos - 1)))<br />
<nowiki> </nowiki> nome_arquivo_tabela = ("tabela-%i-%i.txt" % (indice,(indice + qt_ips_publicos - 1)))<br />
<nowiki> </nowiki> # calcula a máscara das subnets privadas baseado na relação PRI/PUB:<br />
<nowiki> </nowiki> subnets_privadas = list(rede_privada.subnets(new_prefix=relacao_mascara[relacao]))<br />
<nowiki> </nowiki> subnets_publicas = list(rede_publica.subnets(new_prefix=relacao_mascara[relacao]))<br />
except:<br />
<nowiki> </nowiki> print("\nErro! Informe parâmetros válidos para este script:\n\nRespeite a relação de IP público x IP privado: 1:32, 1:16, 1:8, etc\n\nEncerrando!\n")<br />
<nowiki> </nowiki> exit(0)<br />
<br />
if (qt_ips_publicos * relacao_ips_masq[relacao]) > qt_ips_privados:<br />
<nowiki> </nowiki> print("\nErro! Quantidade de IPs privados insuficiente!")<br />
<nowiki> </nowiki> exit(0)<br />
<br />
print(" - Índice das regras: %i;" % (indice))<br />
print(" - Rede pública: %s (%i IPs);" % (txt_publico,qt_ips_publicos))<br />
print(" - Rede privada: %s (%i IPs);" % (txt_privado,qt_ips_privados))<br />
print(" - Quantidade de IPs privados por IP público: %i (%i sub-redes /%i);" % (qt_ips_privados_por_ip_publico, qt_ips_publicos, relacao_mascara[relacao]))<br />
print(" - Total de portas públicas: %i;" % (qt_total_portas))<br />
print(" - Portas por IP privado: %i;" % (qt_portas_por_ip))<br />
print(" - Arquivo de destino (conf): '%s';" % (nome_arquivo_regras))<br />
print(" - Arquivo de tabela (txt): '%s';" % (nome_arquivo_tabela))<br />
print("\n")<br />
<br />
if fazer_regras_in:<br />
<nowiki> </nowiki> print("\nATENÇÃO!\n Variável fazer_regras_in=True\n Mesmo não sendo boas práticas, SERÃO geradas regras de CGNAT do tipo IN!\n")<br />
<br />
<nowiki>#</nowiki>------------------------------------------------- Abre o arquivo onde as regras serão armazenadas (destino) e tabela de relacionamento de portas (tabela):<br />
try:<br />
<nowiki> </nowiki> caminho_deste_script = os.path.dirname(os.path.realpath(__file__))+'/'<br />
<nowiki> </nowiki> arquivo_regras = open(caminho_deste_script+nome_arquivo_regras, "w")<br />
except (OSError, IOError) as e:<br />
<nowiki> </nowiki> print ("\nErro!\nFalha ao abrir a escrita do arquivo onde as regras serão armazenadas (destino)")<br />
<nowiki> </nowiki> sys.exit(1)<br />
<br />
try:<br />
<nowiki> </nowiki> caminho_deste_script = os.path.dirname(os.path.realpath(__file__))+'/'<br />
<nowiki> </nowiki> arquivo_tabela = open(caminho_deste_script+nome_arquivo_tabela, "w")<br />
except (OSError, IOError) as e:<br />
<nowiki> </nowiki> print ("\nErro!\nFalha ao abrir a escrita do arquivo onde as regras serão armazenadas (tabela)")<br />
<nowiki> </nowiki> sys.exit(1)<br />
<br />
arquivo_regras.write("# %s\n" %(titulo))<br />
arquivo_regras.write("# - blocos %s -> %s;\n# - %i de IPs privados / IP público;\n# - %i portas / IP privado;\n" %(<br />
<nowiki> </nowiki> txt_privado,<br />
<nowiki> </nowiki> txt_publico,<br />
<nowiki> </nowiki> qt_ips_privados_por_ip_publico,<br />
<nowiki> </nowiki> qt_portas_por_ip<br />
))<br />
<br />
arquivo_tabela.write("# %s\n" %(titulo))<br />
arquivo_tabela.write("# - blocos %s -> %s;\n# - %i de IPs privados / IP público;\n# - %i portas / IP privado;\n" %(<br />
<nowiki> </nowiki> txt_privado,<br />
<nowiki> </nowiki> txt_publico,<br />
<nowiki> </nowiki> qt_ips_privados_por_ip_publico,<br />
<nowiki> </nowiki> qt_portas_por_ip<br />
))<br />
<br />
<nowiki>#</nowiki>-------------------------------------------------------------------------- principal<br />
<br />
if sys.version_info >= (3,0):<br />
<nowiki> </nowiki> input("Tecle [ENTER]...")<br />
else:<br />
<nowiki> </nowiki> raw_input("Tecle [ENTER]...")<br />
momento_incial = time.time()<br />
<br />
print("\n")<br />
indice_subnet_privada = 0<br />
indice_subnet_publica = 0<br />
<nowiki>#</nowiki> Zera o range de portas para o prox IP publico<br />
porta_ini = numero_porta_incial<br />
<nowiki>#</nowiki> guarda valor de indice inicial<br />
porta_fim = (numero_porta_incial + (qt_portas_por_ip -1))<br />
for ip_publico in rede_publica:<br />
<nowiki> </nowiki> arquivo_regras.write("# %s #INDICE %i / PREFIXO PUBLICO %s\n" % ('-' * 40, indice, str(ipaddress.ip_network(subnets_publicas[indice_subnet_publica]))))<br />
<nowiki> </nowiki> print("VALOR: %s" % (str(ipaddress.ip_network(subnets_publicas[indice_subnet_publica]))))<br />
<nowiki> </nowiki> arquivo_regras.write("add chain ip nat CGNATOUT_%i\n" % (indice))<br />
<nowiki> </nowiki> if fazer_regras_in:<br />
<nowiki> </nowiki> arquivo_regras.write("add chain ip nat CGNATIN_%i\n" % (indice))<br />
<nowiki> </nowiki> arquivo_regras.write("flush chain ip nat CGNATOUT_%i\n" % (indice))<br />
<nowiki> </nowiki> if fazer_regras_in:<br />
<nowiki> </nowiki> arquivo_regras.write("flush chain ip nat CGNATIN_%i\n" % (indice))<br />
<nowiki> </nowiki> subnetpriv = subnets_privadas[indice_subnet_privada]<br />
<nowiki> </nowiki> subnetpub = subnets_publicas[indice_subnet_publica]<br />
<nowiki> </nowiki> print("%s INDICE=%i - PREFIXO_PUBLICO=%s -> SUBNET_PRIVADA_%i=%s" % ("=" * 40, indice, str(ipaddress.ip_network(subnets_publicas[indice_subnet_publica])), (indice_subnet_privada+1), str(subnetpriv)))<br />
<nowiki> </nowiki> ip_privado = ipaddress.ip_network(subnetpriv)<br />
<nowiki> </nowiki> ip_publico = ipaddress.ip_network(subnetpub)<br />
<nowiki> </nowiki> trp = "%i-%i" % (porta_ini,porta_fim)<br />
<nowiki> </nowiki> print("%s IP PRIVADO %s:%s" %("-"*60,str(ip_privado),trp))<br />
<nowiki> </nowiki> #Regras para montagem do netmap<br />
<nowiki> </nowiki> arquivo_regras.write("%s CGNATOUT_%i meta l4proto { tcp, udp } counter snat ip prefix to ip saddr map { %s : %s . %s }\n" % (<br />
<nowiki> </nowiki> __com1__,<br />
<nowiki> </nowiki> indice,<br />
<nowiki> </nowiki> str(ip_privado),<br />
<nowiki> </nowiki> str(ip_publico),<br />
<nowiki> </nowiki> trp<br />
<nowiki> </nowiki> ))<br />
<br />
<nowiki> </nowiki> if fazer_regras_in:<br />
<nowiki> </nowiki> arquivo_regras.write("%s CGNATIN_%i meta l4proto { tcp, udp } counter dnat ip prefix to ip daddr map { %s : %s . %s }\n" % (<br />
<nowiki> </nowiki> __com1__,<br />
<nowiki> </nowiki> indice,<br />
<nowiki> </nowiki> str(ip_publico),<br />
<nowiki> </nowiki> str(ip_privado),<br />
<nowiki> </nowiki> trp<br />
<nowiki> </nowiki> ))<br />
<br />
<nowiki> </nowiki> arquivo_regras.write("%s CGNATOUT_%i counter snat ip prefix to ip saddr map { %s : %s }\n" % (<br />
<nowiki> </nowiki> __com1__,<br />
<nowiki> </nowiki> indice,<br />
<nowiki> </nowiki> str(ip_privado),<br />
<nowiki> </nowiki> str(ip_publico)<br />
<nowiki> </nowiki> ))<br />
<nowiki> </nowiki> arquivo_regras.write("%s CGNATOUT ip saddr %s counter jump CGNATOUT_%i\n" % (<br />
<nowiki> </nowiki> __com1__,<br />
<nowiki> </nowiki> str(subnetpriv),<br />
<nowiki> </nowiki> indice<br />
<nowiki> </nowiki> ))<br />
<nowiki> </nowiki> if fazer_regras_in:<br />
<nowiki> </nowiki> arquivo_regras.write("%s CGNATIN ip daddr %s counter jump CGNATIN_%i\n" % (<br />
<nowiki> </nowiki> __com1__,<br />
<nowiki> </nowiki> str(ip_publico),<br />
<nowiki> </nowiki> indice<br />
<nowiki> </nowiki> ))<br />
<br />
<nowiki> </nowiki> # Gera tabela de corelacao de IPs e portas para quebra de sigilo tecnologico<br />
<nowiki> </nowiki> for ip_publico, ip_privado in zip(ipaddress.ip_network(subnetpub),ipaddress.ip_network(subnetpriv)):<br />
<nowiki> </nowiki> arquivo_tabela.write("%s \t %s \t %s\n" % (str(ip_privado),str(ip_publico),trp))<br />
<br />
<nowiki> </nowiki> indice+=1<br />
<nowiki> </nowiki> indice_subnet_privada+=1<br />
<nowiki> </nowiki> print("porta: %i porta: %i" % (porta_ini,porta_fim))<br />
<nowiki> </nowiki> #incrementa o range de portas para o próximo IP privado<br />
<nowiki> </nowiki> porta_ini += qt_portas_por_ip<br />
<nowiki> </nowiki> porta_fim += qt_portas_por_ip<br />
<nowiki> </nowiki> if porta_fim > numero_porta_final:<br />
<nowiki> </nowiki> # muda subnet publica e inicializa portas<br />
<nowiki> </nowiki> indice_subnet_publica+=1<br />
<nowiki> </nowiki> porta_ini = numero_porta_incial<br />
<nowiki> </nowiki> porta_fim = (numero_porta_incial + (qt_portas_por_ip -1))<br />
<br />
<nowiki>#</nowiki>-------------------------------------------------------------------------- final<br />
<br />
<nowiki>#</nowiki>Fecha o arquivo onde as regras serão armazenadas e a tabela de relacionamento:<br />
try:<br />
<nowiki> </nowiki> arquivo_regras.close()<br />
except (OSError, IOError) as e:<br />
<nowiki> </nowiki> print ("\nErro!\nFalha ao salvar o arquivo onde as regras serão armazenadas (destino)")<br />
<nowiki> </nowiki> sys.exit(1)<br />
<br />
try:<br />
<nowiki> </nowiki> arquivo_tabela.close()<br />
except (OSError, IOError) as e:<br />
<nowiki> </nowiki> print ("\nErro!\nFalha ao salvar o arquivo onde as regras serão armazenadas (tabela)")<br />
<nowiki> </nowiki> sys.exit(1)<br />
<br />
print("\nFIM!\n\nAs regras foram geradas no arquivo:\n%s\n\nTabela salva no arquivo:\n%s\n\nDuração: %.3f segundos" % (<br />
<nowiki> </nowiki> caminho_deste_script + nome_arquivo_regras,<br />
<nowiki> </nowiki> caminho_deste_script + nome_arquivo_tabela,<br />
<nowiki> </nowiki> (time.time()-momento_incial)<br />
))<br />
Para gerar as regras é só executar da mesma maneira. Exemplo:<pre><br />
# ./cgnat-nft-netmap.py 0 198.18.0.0/27 100.64.0.0/22 1/32<br />
</pre>Abaixo exemplos de como ficam as novas regras e na memória:<br />
[[Arquivo:Nftables netmap1.png|nenhum|commoldura|926x926px]]<br />
[[Arquivo:Nftables netmap2.png|nenhum|commoldura|929x929px]]<br />
Exemplo do arquivo '''tabela-0-31.txt''' que foi gerado:<br />
[[Arquivo:Nftables netmap3.png|nenhum|commoldura|963x963px]]<br />
<br />
=== Simulando um acesso do cliente e observando os resultados ===<br />
Para testar as regras, foi criado um ambiente virtual de laboratório usando um '''Proxmox''' e criando 3 VMs: '''CGNAT''', '''BNG''' e '''CLIENTE'''. Do router de testes capturei os pacotes para demonstrar como funciona o CGNAT. A seguir teremos o acesso por parte do cliente e a captura dos pacotes somente para uma '''POC (Proof of Concept)''', para demonstrar que o CGNAT está funcionando e alocando a porta, dentro do range de portas, corretamente para um determinado cliente.<br />
<br />
Abaixo temos um exemplo de captura bem simples de pacote mostrando que o '''IP 198.18.0.0''' com '''porta origem''' '''6767/TCP''' acessou o '''200.147.41.220 na porta 443/TCP''', um acesso para o site do '''UOL'''.<br />
[[Arquivo:Cgnat sniffer.png|nenhum|miniaturadaimagem|1039x1039px]]<br />
Se olharmos os dados marcados acima e procurarmos pelo IP '''198.18.0.0 e porta 6767''' no nosso arquivo de configuração do CGNAT, '''acharemos o IP 100.64.0.2''' '''que utiliza o range de portas entre 5056 e 7071'''. Abaixo o nosso arquivo de regras de CGNAT para comprovar o range de portas utilizados.<br />
[[Arquivo:Regras5.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Monitorando o tráfego em tempo real ===<br />
Monitorando o '''tráfego Mbps/PPS''' com a ferramenta '''bmon'''. Para instalar o software no Debian basta fazer: <br />
# apt install bmon<br />
Para monitorar as interfaces faríamos algo assim onde '''-b''' para '''bits/s''' e o '''-p''' para '''selecionar as interfaces que quer monitorar'''. Para monitorar nosso '''bond0''' e '''bond1''' o comando seria esse abaixo:<br />
# bmon -b -p bond0,bond1<br />
Abaixo uma tela de exemplo do '''bmon''' em execução:<br />
[[Arquivo:Bmon cgnat.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
== CGNAT no Mikrotik RouterOS ==<br />
Uma boa opção para caixa CGNAT com custo x benefício acessível seria uma '''CCR1036-8G-2S+''' onde se for configurada somente para fazer '''CGNAT''', com o '''mínimo de regras de filtro''' e '''Fasttrack habilitado''', já alcancei '''13 Gbps de tráfego ou 26 Gbps agregado''' fazendo um '''bonding com as 2 interfaces ópticas de 10Gbps'''.<br />
<br />
Essa imagem abaixo foi retirada do '''datasheet da CCR1036-8G-2S+''':<br />
[[Arquivo:Datasheet ccr1036.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Configurando o sistema ===<br />
Instale um '''Mikrotik RouterOS do zero''', procure utilizar a '''versão mais estável possível'''. Como não utilizei ainda em produção o RouterOS 7.x, '''sugiro utilizar a versão 6.48.6 Long-term''', que até o momento, é a versão considerada mais estável. O processo de configurar um '''CGNAT Determinístico no Mikrotik RouterOS''' será bem mais simples que no '''Debian GNU/Linux''' mas a capacidade alcançada com o '''GNU/Linux''' será bem superior ao visto aqui.<br />
<br />
=== Sobre Fasttrack ===<br />
O '''Fasttrack''' é um recurso muito importante que aumentará a performance da sua caixa CGNAT, '''acelerando o encaminhamento de pacotes''' e '''diminuindo o consumo de CPU'''. Neste momento não faremos isso. Quando chegarmos no processo de criação das regras de CGNAT, ele será habilitado e será mostrado quais as regras que fazem isso.<br />
<br />
=== Configurando o bonding ===<br />
Como usaremos as '''duas portas de 10GbE sfp+ da CCR''', utilizaremos vlans para separar a rede que se comunicará com a Internet, da rede com o BNG. A seguir veremos como deixar o nosso bonding. Na sequência configuramos nossas vlans de entrada e saída e em cima delas '''os IPs do diagrama''', como fizemos com o Debian. Vamos definir a '''vlan 101''' '''para a interface que fará a comunicação com a Internet''' e '''por onde será feito o CGNAT''' e a '''vlan 102 que fará a comunicação com o BNG'''.<br />
[[Arquivo:Cgnat mk1.png|nenhum|miniaturadaimagem|1043x1043px]]<br />
[[Arquivo:Cgnat mk2.png|nenhum|miniaturadaimagem|1237x1237px]]<br />
<br />
=== Configurando os IPs e rotas ===<br />
O objetivo deste artigo é ser bem simples para entendermos os conceitos e por isso estamos utilizando '''rotas estáticas''' e não estamos envolvendo outros protocolos como o '''OSPF'''. Nada impediria de utilizar a mesma técnica apresentada aqui em um cenário com '''OSPF''', por exemplo.<br />
<br />
A seguir veremos que na '''vlan-101-borda''' configuramos o '''IP 10.0.10.172/24''' e na '''vlan-102-bng''' configuramos o '''IP 192.168.0.1/24'''.<br />
<br />
Como rotas criamos uma '''default route''' apontando para o '''IP 10.0.10.1''', criamos uma rota para '''100.64.0.0/22''' com '''next-hop 192.168.0.2''' e para nos '''protegermos de static loop''' teremos nossas rotas de '''blackhole''' quando formos gerar as regras de CGNAT.<br />
<br />
Na imagem aparece como '''unreachable''' porque esse equipamento, que está sendo usado como lab, não está conectado em uma switch.<br />
[[Arquivo:Cgnat mk3.png|nenhum|miniaturadaimagem|1040x1040px]]<br />
<br />
=== Recomendações de segurança ===<br />
<br />
* Utilize credenciais de acesso '''com senhas fortes''', não esqueça o login admin sem senha (padrão no Mikrotik RouterOS).<br />
<br />
* Desabilite todos os serviços que não for utilizar e os que ficarem abertos, especifique neles o acesso apenas da sua rede de gerência. Não deixe qualquer serviço aberto para a Internet.<br />
<br />
* Habilite o '''TCP SynCookies'''.<br />
<br />
Procure criar suas regras de filtros de pacotes sempre na '''Table Raw''', ela não agride tanto a performance do equipamento mas '''necessita de muita atenção''' porque ela pode afetar os acessos dos assinantes. Isso porque uma regra genérica demais será analisada tanto com destino a caixa, quanto destino ao cliente e o mesmo pode ocorrer no sentido inverso, do cliente para a Internet.<br />
[[Arquivo:Cgnat mk4.png|nenhum|miniaturadaimagem|1041x1041px]]<br />
<br />
=== Acertando data e hora ===<br />
Configure o '''NTP client da caixa''' e mantenha a data e horário sincronizados.<br />
[[Arquivo:Cgnat mk5.png|nenhum|miniaturadaimagem|1042x1042px]]<br />
<br />
=== Criando as regras de CGNAT ===<br />
Para simplificar nossa vida, '''Rudimar Remontti''' criou em seu blog, um sistema para gerar regras de '''CGNAT Determinístico''' de forma simples e performática, '''utilizando regras netmap da Mikrotik'''. Para tanto o link é este:<br />
<br />
'''https://cgnat.remontti.com.br/'''<br />
<br />
O sistema é bem completo, simples, irá gerar as '''regras de CGNAT''' e nossas '''blackholes''' para '''bloqueio de static loop'''. Também no final teremos uma '''tabela de associação''' que devemos guardar para fazer as quebras de sigilo solicitadas nos Ofícios Judiciais.<br />
<br />
Ao acessar o site e seguindo o nosso diagrama completaremos as informações conforme mostrado a seguir.<br />
[[Arquivo:Cgnat remontti1.png|nenhum|miniaturadaimagem|1047x1047px]]<br />
O site irá gerar automaticamente os comandos de onde faremos uma cópia e executaremos no nosso equipamento '''Mikrotik RouterOS'''.<br />
[[Arquivo:Cgnat remontti2.png|nenhum|miniaturadaimagem|1049x1049px]]<br />
No final da página é gerado uma tabela do mapeamento das portas, isso deve ser salvo como documento importante pois será usado para quebra de sigilo tecnológico.<br />
[[Arquivo:Cgnat remontti3.png|nenhum|miniaturadaimagem|1052x1052px]]<br />
O conceito é o mesmo, quebrar as regras em blocos menores para chegarmos no nosso '''First Match Win mais rápido''' e não termos que percorrer todas as regras em memória.<br />
[[Arquivo:Cgnat remontti4.png|nenhum|miniaturadaimagem|1053x1053px]]<br />
Abaixo como ficaram as regras que habilita o '''Fasttrack''' no nosso equipamento, aumentando em muito a performance de encaminhamento dos pacotes.<br />
[[Arquivo:Cgnat mk6.png|nenhum|miniaturadaimagem|1056x1056px]]<br />
<br />
== Conclusão ==<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Portas_de_Amplifica%C3%A7%C3%A3o_DDoS_e_Botnets&diff=3507Portas de Amplificação DDoS e Botnets2023-06-29T16:51:41Z<p>Gondim: </p>
<hr />
<div>{{DISPLAYTITLE:Portas de Amplificação DDoS e Botnets}}<br />
__TOC__<br />
[[Arquivo:Criminal-bandit-hacker-scaled-1.jpg|miniaturadaimagem|nenhum|459x459px]]<br />
<br><br />
==Introdução==<br />
Para contextualizar nosso artigo precisamos entender basicamente que: '''amplificação''' é quando perguntamos algo para algum serviço (porta) e a resposta retornada, é maior que o tamanho da pergunta. Tendo isso em mente, existem alguns '''serviços abertos na Internet''' com essa característica e são explorados para realizarem o que conhecemos como '''Ataques de''' '''DDoS (Distributed Denial of Service)'''. Sabem aqueles ataques que costumam derrubar nossa '''Operação de Internet''', causando indisponibilidade de acesso para nossos assinantes? São esses serviços mal configurados e abertos na Internet, que munem fortemente os cibercriminosos, que usam esses serviços abertos como ferramentas para esse propósito. Não se engane se você acha que na sua Rede, não possa existir esses serviços abertos e que ninguém estaria usando-os para executar esses ataques. Estamos acostumados a sempre reclamar de ataques recebidos, mas você já parou para pensar que você pode estar sendo usado como '''vetor de ataque''' e com isso, '''inconscientemente''', atacando alguém na Internet? Tirando o fato da imperícia de quem configurou erradamente o serviço, deixando-o '''vulnerável''', '''exposto''' e '''suscetível''' a ser explorado, essa pessoa é tanto vítima quanto você.<br />
<br />
== Consequências ==<br />
Quando somos usados como '''vetor de ataque''', nossa Rede se torna instável, nossos clientes reclamam de lentidão no acesso à Internet, nosso '''Call Center''' fica atarefado com solicitações que provavelmente eles não conseguirão identificar a causa raiz do problema. Como resultado teremos um aumento da insatisfação dos nossos clientes e muitos cancelamentos ('''churn'''). Para evitarmos todos esses males, resolvi escrever esse artigo dando dicas e explicando maneiras de fixarmos esses problemas.<br />
<br />
Quanto às '''Botnets,''' existem sistemas que foram comprometidos e podem estar sendo explorados para alguma atividade ilícita e que também poderá prejudicar a sua Operação de Internet. Esses casos também precisam ser tratados.<br />
<br />
==Portas de Amplificação e de Botnets mais conhecidas==<br />
{| class="wikitable"<br />
|+<br />
!'''Porta'''<br />
!Descrição<br />
|-<br />
|DNS (53/udp)<br />
|Servidores de DNS Recursivos abertos para o mundo. Não podemos confundir com o serviço de DNS Autoritativo (53/UDP/TCP), este precisa estar aberto à consultas dos domínios de sua autoridade.<br />
|-<br />
|SNMP (161/udp)<br />
|'''Simple Network Management Protocol''' ou como costumo brincar dizendo que o significado seria: '''Security is Not My Problem'''. Este serviço é muito usado para monitorarmos nossos ativos de Redes e Servidores.<br />
|-<br />
|NTP (123/udp)<br />
|'''Network Time Protocol''' é um serviço bastante importante para mantermos nossos sistemas com data e hora corretos.<br />
|-<br />
|SSDP (1900/udp)<br />
|'''Simple Service Discovery Protocol'''.<br />
|-<br />
|PORTMAP (111/udp)<br />
|Daemon que atribui portas dinamicamente para serviços '''RPC''' '''(Remote Procedure Call)''' como '''NIS (Network Information Service)''' e o '''NFS (Network File System)''' comumente usados em sistemas Unix Like.<br />
|-<br />
|NETBIOS (137/udp)<br />
|'''Network Basic Input/Output System''', faz parte dos serviços de compartilhamento de Redes baseadas em Microsoft Windows.<br />
|-<br />
|UBNT (10001/udp)<br />
|Serviço de '''Device Discovery''' habilitado nos equipamentos da Ubiquiti.<br />
|-<br />
|MDNS (5353/udp)<br />
|Multicast DNS.<br />
|-<br />
|LDAP (389/udp)<br />
|Serviço '''Lightweight Directory Access Protocol'''.<br />
|-<br />
|CHARGEN (19/udp)<br />
|'''Character Generator Protocol''', é um protocolo usado para fins de teste, depuração e medição. Esse serviço gera datagramas contendo um número aleatório de caracteres entre 0 e 252, sempre que requisitado.<br />
|-<br />
|QOTD (17/udp)<br />
|'''Quote Of The Day''' outro serviço de teste e medição.<br />
|-<br />
|MEMCACHED (11211/udp)<br />
|Serviço de cache para aceleração de aplicativos Web.<br />
|-<br />
|WS-DISCOVERY (3702/udp)<br />
|'''Web Services Dynamic Discovery Protocol''', é um protocolo de descoberta multicast para localizar serviços em uma Rede local.<br />
|-<br />
|TFTP (69/udp)<br />
|Trivial File Transfer Protocol, é um protocolo bastante utilizado por ativos de redes para por exemplo, transferir uma atualização de firmware.<br />
|-<br />
|CoAP (5683/udp)<br />
|'''Constrained Application Protocol''', é um protocolo de comunicação usado por dispositivos de Internet que possuem pouco recurso de hardware.<br />
|-<br />
|ARMS (3283/udp)<br />
|'''Apple Remote Management Service''', serviço utilizado pela Apple para prover acesso remoto e gerenciamento de dispositivos rodando MacOS.<br />
|-<br />
|DHCPDiscover (37810/udp)<br />
|'''Dynamic Host Configuration Protocol Discover''', protocolo utilizado para gerenciar equipamentos DVR.<br />
|-<br />
|MT4145 (4145/tcp)<br />
|Serviço de SOCKS habilitado na porta 4145/tcp, normalmente visto em equipamentos Mikrotiks comprometidos e abusados por Botnet.<br />
|-<br />
|MT5678 (5678/tcp)<br />
|Serviço de SOCKS4 habilitado na porta 5678/tcp, visto em Mikrotiks comprometidos e sendo abusados pela Meris Botnet .<br />
|}<br />
<br />
== Como saber se temos Portas de Amplificação DDoS e de Botnets em nossa Rede? ==<br />
Primeiramente saiba que existem entidades que se preocupam com a segurança da '''Internet''' e que tem como objetivo extinguir a exposição desses serviços de amplificação da '''Internet'''. Uma entidade que podemos citar é o [https://www.shadowserver.org/news/the-scannings-will-continue-until-the-internet-improves/ '''ShadowServer'''] e a outra já conhecemos muito bem, que é o nosso valoroso [https://bcp.nic.br/i+seg/acoes/amplificacao/ '''CERT.br''']. Uma maneira bem fácil e simples de sermos reportados sobre esses problemas de segurança, é mantendo nosso '''Contato de Segurança''' atualizado no '''Registro.br'''. Mostrarei abaixo em telas de exemplo o quanto isso é fácil. Você precisará de ter a '''credencial de acesso do seu ASN''' no [https://registro.br/ '''Registro.br'''] e já ter criado um '''ID com seus dados''' e '''e-mail,''' que será usado pelo '''CERT.br''' para enviar as '''notificações de incidentes'''.<br />
[[Arquivo:Registrobr1.png|miniaturadaimagem|691x691px|esquerda]][[Arquivo:Registrobr6.png|miniaturadaimagem|691x691px|nenhum]]<br />
[[Arquivo:Registrobr3.png|miniaturadaimagem|691x691px|esquerda]][[Arquivo:Registrobr4.png|nenhum|miniaturadaimagem|691x691px]]<br />
[[Arquivo:Registrobr7.png|esquerda|miniaturadaimagem|691x691px]][[Arquivo:Registrobr5.png|miniaturadaimagem|691x691px|nenhum]]<br />
<br />
<br><br />
<br><br />
<br><br />
== O que vem depois ==<br />
Após acertar seu contato de segurança, você começará a receber e-mails do '''CERT.br''' e de '''outros ASNs''' sempre que houver algum incidente de segurança envolvendo o seu ASN e '''notificações''' sobre as '''portas de amplificação/botnets''', abertas em sua Rede, inclusive com cada e-mail referente a qual porta está aberta, com o esclarecimento técnico sobre os riscos e instruções de como testar e checar se o problema foi resolvido. Perceba que agora você não estará mais cego quanto aos problemas de segurança que sua Rede vem sofrendo e tenha certeza de que quanto mais limpa ela estiver, mais qualidade de serviço estará entregando ao seu cliente, diminuindo a quantidade de chamados em seu Call Center e consequentemente diminuindo seu '''churn'''.<br />
<br />
Um exemplo de e-mail que você receberá para cada serviço aberto:<br />
[[Arquivo:Certbr.png|nenhum|miniaturadaimagem|1122x1122px]]<br />
<br />
Até aqui te expliquei sobre o problema, o que ele pode causar ao seu negócio e te mostrei como passar a enxergá-los. Mas como tratá-los?<br />
<br />
== Tratando a causa raiz ==<br />
Como alguns devem ter percebido, nessa nossa relação de portas de amplificação existem portas que se forem bloqueadas te causarão dor de cabeça e muitas reclamações. Vou citar apenas como exemplo: '''DNS''', '''SNMP''' e '''NTP'''. Três serviços que se você não tiver cuidado, poderá impactar seus assinantes. Temos que entender que podemos ter 2 tipos de clientes: o '''residencial''' e o '''corporativo'''. O cliente residencial é mais simples de se resolver porque provavelmente ele não terá um servidor DNS rodando em sua residência, nem um SNMP e tão pouco um servidor NTP, porém o serviço de NTP não pode simplesmente ser bloqueado devido sua natureza de funcionamento e necessidade dos dispositivos, na residência, de manterem-se com a data e horário corretos.<br />
<br />
Já o cliente corporativo pode possuir todos esses serviços rodando e nesse caso faz-se necessário um maior entendimento junto ao cliente. O que quero dizer com isso é que para ambos os casos, você precisa elaborar um '''Plano de Ação''' para tratar cada caso, buscando sempre o resultado mais eficaz e sem causar indisponibilidade para seu cliente. O que vou propor nesse artigo são duas das formas de se tratar o problema, mas precisamos ter bom senso na hora da decisão e termos certeza de como implementar a solução tecnicamente.<br />
<br />
A informação contendo os IPs da nossa Rede que estão com as Portas de Amplificação abertas já temos em mãos, agora precisamos tratá-la. Tenho 2 propostas em mente:<br />
<br />
* Identificar os clientes com as falhas de segurança e contatá-los um a um, explicar sobre o problema, o que isso pode afetá-los e ajudá-los a resolver a situação. Essa seria a maneira mais correta, uma boa prática, porque implica em melhorar a cultura de segurança, fazendo todos entenderem sobre os riscos envolvidos e aumentando o senso de cuidado. Em contra partida é uma ação muito trabalhosa e dependendo da quantidade de clientes pode ser muito demorada.<br />
* Aplicar filtros e controles de acordo com o perfil do cliente. Para se ter um resultado mais rápido e efetivo podemos aplicar filtros em nossos '''BNGs''', bloqueando as Portas de Amplificação '''sempre no sentido Internet para o cliente''', mas com alguns cuidados que citarei ainda. Observando sempre os casos de clientes corporativos e que podem necessitar de um tratamento separado.<br />
<br />
== Estratégia de bloqueio das Portas de Amplificação/Botnet ==<br />
Faremos o bloqueio de todas as Portas de Amplificação citadas na tabela desse artigo, sempre no sentido Internet para o cliente. Isso é muito importante! O '''serviço NTP''' terá um tratamento diferenciado porque não podemos apenas bloquear o serviço, precisaremos fazer um '''rate-limit''' e uma '''condição especial''' para que seja feito o drop dos pacotes. Como a quantidade de vendors é grande e cada Provedor escolhe o melhor para a sua Operação, trouxe aqui como exemplo regras para '''Mikrotik RouterOS''', basta entender a lógica como vou explicar e procurar fazer o mesmo em seu equipamento, seja de qual fabricante for. Escolhi este vendor como exemplo pois além de ser muito utilizado no mercado, devido ao seu custo x benefício, será mais fácil de explicar o funcionamento. Entendendo como funciona, você pode implementar no seu Cisco, Huawei, Juniper, etc.<br />
<br />
== As regras de bloqueio no RouterOS ==<br />
/ip firewall raw<br />
add action=drop chain=prerouting comment="# BLOQUEIA NTP DE ORIGEM NAO NTP" dst-port=123 in-interface=!all-ppp protocol=udp src-port=!123<br />
add action=drop chain=prerouting comment="# DROP ABUSO NTP" limit=!2M,2M:bit protocol=udp src-port=123<br />
add action=drop chain=prerouting comment="# DROP PORTAS UDP DE AMPLIFICACAO" dst-port=53,161,1900,111,137,10001,5353,389 in-interface=!all-ppp protocol=udp<br />
add action=drop chain=prerouting comment="# DROP PORTAS UDP DE AMPLIFICACAO" dst-port=19,17,11211,3702,69,5683,3283,37810 in-interface=!all-ppp protocol=udp<br />
add action=drop chain=prerouting comment="# DROP PORTAS TCP BOTNET" dst-port=4145,5678 in-interface=!all-ppp protocol=tcp tcp-flags=syn<br />
<br />
Estamos fazendo os bloqueios na tabela '''raw''' por ocupar menos processamento do sistema e sabemos o quanto isso é importante no RouterOS. Essas regras estão sendo aplicadas em um BNG concentrador PPPoE onde:<br />
<br />
* Na primeira linha '''ADD''' estamos bloqueando qualquer pacote entrando por qualquer '''interface que''' '''não seja PPP''', ou seja, que viria da Internet sentido o cliente, que está atrás de uma interface do tipo PPP. Ainda analisando esse filtro, somente entraria nessa regra se a '''porta origem''' do pacote '''não for a 123/udp''' e com '''destino a porta''' '''123/udp'''. Temos que concordar que se um dispositivo do cliente solicitar um sincronismo com algum servidor NTP na Internet, este responderá através da porta 123/udp. Não deve chegar para o cliente uma resposta com origem diferente da porta 123/udp. Por que isso? Algumas implementações do serviço NTP disparam requisições com porta origem 123 ao invés de uma porta origem >= 1024.<br />
* Na segunda linha '''ADD''' estamos fazendo um '''rate-limit de até 2Mbps''' para qualquer pacote com a '''porta origem 123/udp''' e para '''qualquer destino'''. Para evitar que a exploração da vulnerabilidade do serviço cause excesso de tráfego na conexão.<br />
* A terceira linha '''ADD''' bloqueamos o restante das portas udp de amplificação mas com a seguinte condição: pacotes que entrem por qualquer '''interface que não seja PPP''', novamente definindo o sentido Internet para o cliente e que as portas de destino sejam a nossa lista restante de portas de amplificação.<br />
* Por último no quarto '''ADD''', bloqueamos as portas '''TCP de Botnet 4145 e 5678''', de pacotes que entrem por qualquer interface que não seja do tipo PPP, com destino elas mas com uma condição do '''pacote estar setado com a tcp flag SYN''', que indica um pedido de início de conexão. Se tem alguém na Internet tentando se conectar nessas portas TCP e o sistema por trás for um RouterOS, algo não está bem.<br />
<br />
== Checando se o problema foi resolvido ==<br />
Após aplicarmos os filtros, checaremos se os IPs estão com as portas de amplificação fechadas e para isso fiz um '''shell script em bash''', que usa alguns pacotes para validarmos o serviço. Como sempre estou usando para os nossos artigos a distribuição Debian GNU/Linux. Procure separar um ambiente para instalar esses pacotes e servir como base de testes. O ideal é que esteja de fora na Internet e sem qualquer bloqueio de Operadoras para esses testes poderem funcionar. Para usar o '''rpcinfo''' precisaremos do pacote '''rpcbind''' instalado, que após a instalação levantará o serviço rpc (111/udp) e na sequência iremos desabilitá-lo.<br />
# apt install nmap bind9-host bind9-dnsutils bsdextrautils netcat snmp samba-common-bin rpcbind ntp ldap-utils curl<br />
<br />
# systemctl stop rpcbind.service<br />
# systemctl stop rpcbind.socket<br />
# systemctl disable rpcbind.service<br />
# systemctl disable rpcbind.socket<br />
O script dei o nome de '''amplicacao.sh''' mas fica ao seu critério chamá-lo como quiser. Ele se encontra aqui no Github: '''https://github.com/gondimcodes/amplificacao'''<br />
<br />
Para usar é bem simples. No exemplo abaixo fiz um cheque no 8.8.8.8:<br />
[[Arquivo:Amplificacao.png|nenhum|miniaturadaimagem|654x654px]]<br />
Se você quiser checar apenas um serviço se está aberto, podemos fornecer após o IP, o nome do serviço queremos testar seguindo essa lista de opções:<br />
<br />
* netbios<br />
* rpc<br />
* arms<br />
* tftp<br />
* dns<br />
* mdns<br />
* ssdp<br />
* snmp<br />
* ntp<br />
* ldap<br />
* ubnt<br />
* chargen<br />
* qotd<br />
* memcached<br />
* ws-discovery<br />
* coap<br />
* mt4145<br />
* mt5678<br />
* dhcpdiscover<br />
<br />
Para fazer o teste de apenas um serviço, só observar o exemplo abaixo:<br />
[[Arquivo:Amplificacao2.png|nenhum|miniaturadaimagem|560x560px]]<br />
<br />
== Conclusão ==<br />
Agora que sabemos o que são as Portas de Amplificação DDoS, os riscos para a nossa Operação de Internet, sabemos como ter visibilidade do problema e como checar e tratar esse problema; você ainda vai continuar deixando isso te afetar ou vamos dar aquele UP! na sua Operação e entregar mais qualidade de serviço para seus assinantes?<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Usu%C3%A1rio:Gondim&diff=3506Usuário:Gondim2023-06-29T14:13:31Z<p>Gondim: </p>
<hr />
<div>'''Marcelo Gondim da Cunha'''<br />
[[Arquivo:Gondim paisagem.jpg|esquerda|commoldura]]<br />
<br />
'''Contribuições e trabalhos:'''<br />
<br />
* Administração de Sistemas Unix-Like desde 1996.<br />
* Consultor na Conectiva S/A - Unidade Rio em 2000.<br />
* Autor do projeto TuxFrw - https://tuxfrw.linuxinfo.com.br<br />
* Administração de sistemas BSD pela FreeBSD Brasil em 2010.<br />
* Direção do AS53135 - Nettel Telecomunicações entre 2003 e 2021 atingindo a marca de 41.000 assinantes. Gerando qualidade na entrega de serviços e implantando boas práticas como: <br />
<br />
a) IPv6.<br />
<br />
b) MANRS.<br />
<br />
c) RPKI.<br />
<br />
d) CPEs com firmware baseada na BCOP <nowiki>https://www.m3aawg.org/sites/default/files/lac-bcop-1-m3aawg-v1-portuguese-final.pdf</nowiki><br />
<br />
'''Palestras ministradas:'''<br />
<br />
* Semana da Informática UERJ 2002 - TuxFrw.<br />
* CONISLI 2003 - TuxFrw.<br />
* CONISLI 2004 - Fazendo compras com Gentoo Linux - Palestra sobre a distribuição Linux e suas ferramentas fantásticas.<br />
* CONISLI 2004 - OpenVPN - Palestra sobre como criar VPNs seguras com OpenVPN e diferenças entre ele e o IPSec.<br />
* CONISLI 2005 - SPoP (Security Point of Presence) com OpenVPN - Como utilizar túneis encriptados do OpenVPN para acessar a Internet de forma segura, onde quer que esteja.<br />
* SECOMP 2005 UNIFEI - TuxFrw.<br />
<br />
* Debconf19 2019 - [https://debconf19.debconf.org/talks/4-debian-na-vida-de-uma-operadora-de-telecom/ Debian na vida de uma operadora de Telecom], [https://www.youtube.com/watch?v=vQSTslUZy8k&list=PLYUtdmpYPTTJDtwgD8AtxzFJ9t_URhFMK&index=35 vídeo] e [https://salsa.debian.org/debconf-team/public/share/debconf19/raw/master/slides/4-debian-na-vida-de-uma-operadora-de-telecom.pdf?inline=false pdf].<br />
<br />
* [https://www.youtube.com/watch?v=5uOFtkplDts FiqueEmCasaUseDebian - CGNAT com NFTables] e [https://www.youtube.com/watch?v=Wz2IAg6MMlU SysAdmin apps].<br />
<br />
'''Artigos desenvolvidos para a comunidade do Brasil Peering Fórum:'''<br />
<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_na_pratica CGNAT na pratica].<br />
* [https://wiki.brasilpeeringforum.org/w/Acesso_via_IPv6_Link-Local Acesso via IPv6 Link-Local].<br />
* [https://wiki.brasilpeeringforum.org/w/MANRS MANRS].<br />
* [https://wiki.brasilpeeringforum.org/w/CGNAT_Bulk_Port_Allocation_com_DPDK CGNAT Bulk Port Allocation com DPDK].<br />
* [https://wiki.brasilpeeringforum.org/w/Servidor_de_Logs Servidor de Logs].<br />
* [[DNS Recursivo Anycast Hyperlocal|DNS Anycast com Hyperlocal]].<br />
* [[Recomendações sobre Mitigação DDoS]]<br />
* [[Portas de Amplificação DDoS e Botnets]]<br />
* [[Static Loop - um erro que pode matar seu ISP/ITP]]<br />
<br />
'''Tutorial:'''<br />
<br />
* [https://bit.ly/2saumHK Segurança de roteamento: MANRS (Mutually Agreed Norms for Routing Security) - Tutoriais NIC.br em 09/12/2019].<br />
<br />
'''[https://www.manrs.org/about/advisory-group/members/ MANRS Advisory Group Member (2020-2021)]'''.<br />
<br />
[https://www.youtube.com/watch?v=oahQkGx8urY '''Live sobre MANRS com Leonardo Furtado'''].<br />
<br />
Criação da Wiki ISPUP! em 24/12/2022.<br />
<br />
Semana de Capacitação 6 do NIC.br 28/04/2023 - '''CONCEITOS E IMPLEMENTAÇÃO DE CGNAT'''. Material [https://semanacap.bcp.nic.br/6-online/ aqui] e vídeo aula [https://www.youtube.com/watch?v=1q7J3NkQVSc aqui].<br />
<br />
'''Contatos:'''<br />
<br />
Telegram: '''@Marcelo_Gondim'''<br />
<br />
WhatsApp: +55 (22) 98827-9258<br />
<br />
E-mail: '''gondim at ispup.com.br'''<br />
<br />
Linkedin: https://www.linkedin.com/in/marcelo-gondim-sysadmin/<br />
<br />
Meu Github: https://github.com/gondimcodes<br />
<br />
== Mini-CV ==<br />
'''Marcelo Gondim''' começou sua carreira como desenvolvedor de software em COBOL e Clipper entre 1992 e 1995. Em 1996 foi responsável por desenvolver um sistema concorrente com o RENPAC da Embratel para acesso ao SISCOMEX e implantou a Internet para fins comerciais na empresa DATABRAS. Trabalhou como consultor e instrutor de Linux na Conectiva S/A em 2000. Em 2003 se tornou consultor de diversos Provedores de Internet na Região dos Lagos - RJ e onde acabou se tornando CTO da Nettel Telecomunicações (AS53135) com 42.000 assinantes. Implantou IPv6 iniciando em 2013 e se tornou participante do MANRS com diversas contribuições com artigos e palestras. Atualmente é Especialista em Redes, cuida do SOC (Security Operations Center) da Brasil TecPar AS262907, onde desenvolve as boas práticas, tratamentos de incidentes relacionados ao ASN e desenvolve as estratégias de Mitigação DDoS da empresa. Também desenvolveu uma Rede de DNS Recursivo Anycast espalhada pelo RS e MT/MS, também certificada KINDNS.</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Vazamento_de_prefixos_na_mitiga%C3%A7%C3%A3o_DDoS&diff=3500Vazamento de prefixos na mitigação DDoS2023-06-28T12:35:09Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
== Objetivo ==<br />
Ajudar no troubleshooting de um dos principais problemas relacionados com o não funcionamento de uma Mitigação DDoS, o '''vazamento dos prefixos /24'''. Quando se tem contratado uma nuvem de mitigação DDoS, precisamos garantir que o '''prefixo /24 atacado''' passe pelo '''Scrubbing Center''' da fornecedora de mitigação DDoS. Se o tráfego estiver vazando por outros trânsitos IP, IX, etc ou se o prefixo IP não tiver sendo devidamente anunciado, sua mitigação falhará e o ataque poderá vir por outros caminhos. Embora existam ataques em IPv6, o IPv4 ainda é o principal alvo dos ataques DDoS e por isso esse artigo está focado no IPv4. Provedores que já utilizam IPv6 se beneficiam de '''menor consumo de clean pipe''', '''diminuição de efeitos colaterais causados pela mitigação''' e '''economia em CGNAT'''.<br />
<br />
A maneira mais simples de checarmos os vazamos de '''prefixos /24''', é usando os diversos '''Looking Glass''' que encontramos pela Internet'''.''' Neles podemos ver se os prefixos estão realmente passando por onde devem. Pensando em ajudar nessa tarefa criei um script em python que checa em alguns '''Looking Glass''', como está o anúncio do prefixo e te avisar onde está o problema. O script é simples, não é tão performático porque os sistemas que são consultados também não são rápidos, mas acredito que deve ajudar mesmo assim ou até mesmo servir como prova de conceito para quem está estudando Redes.<br />
<br />
== O script lgview.py ==<br />
Para utilizar o script você precisará do módulo '''netmiko''' instalado. Esse módulo te permite fazer conexões, via python, em diversos tipos de devices. Como utilizaremos o Debian aqui, é bem simples de instalar:<br />
# apt install python3-netmiko<br />
Instalado o '''netmiko''' basta pegar o script no '''Github''' em [https://github.com/gondimcodes/lgview '''https://github.com/gondimcodes/lgview''']. Este script é a primeira versão, pode conter bugs mas sinta-se convidado a ajudar a melhorá-lo. <br />
<br />
Para executar é bem simples, passe como parâmetros: o '''prefixo IPv4 /24''' que quer fazer a busca, o '''ASN da empresa mitigadora''' e o '''ASN do seu prefixo'''. Abaixo um exemplo de chamada:<br />
# ./lgview.py 198.51.100.0/24 65200 65001<br />
[[Arquivo:Lgview.png|nenhum|commoldura]]As linhas em '''vermelho''' merecem sua atenção porque indicam um provável vazamento do prefixo. Em algumas situações pode não ser um vazamento, mas precisa ser checado. Precisamos garantir que o prefixo /24 atacado, esteja passando pela nuvem de mitigação DDoS.<br />
<br />
== Conclusão ==<br />
Espero que além de útil, o artigo possa ter passado o quão é importante essa validação.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Vazamento_de_prefixos_na_mitiga%C3%A7%C3%A3o_DDoS&diff=3499Vazamento de prefixos na mitigação DDoS2023-06-26T03:18:22Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
== Objetivo ==<br />
Ajudar no troubleshooting de um dos principais problemas relacionados com o não funcionamento de uma Mitigação DDoS, o '''vazamento dos prefixos /24'''. Quando se tem contratado uma nuvem de mitigação DDoS, precisamos garantir que o '''prefixo /24 atacado''' passe pelo '''Scrubbing Center''' da fornecedora de mitigação DDoS. Se o tráfego estiver vazando por outros trânsitos IP, IX, etc ou se o prefixo IP não tiver sendo devidamente anunciado, sua mitigação falhará e o ataque poderá vir por outros caminhos. Embora existam ataques em IPv6, o IPv4 ainda é o principal alvo dos ataques DDoS e por isso esse artigo está focado no IPv4. Provedores que já utilizam IPv6 se beneficiam de '''menor consumo de clean pipe''', '''diminuição de efeitos colaterais causados pela mitigação''' e '''economia em CGNAT'''.<br />
<br />
A maneira mais simples de checarmos os vazamos de '''prefixos /24''', é usando os diversos '''Looking Glass''' que encontramos pela Internet'''.''' Neles podemos ver se os prefixos estão realmente passando por onde devem. Pensando em ajudar nessa tarefa criei um script em python que checa em alguns '''Looking Glass''', como está o anúncio do prefixo e te avisar onde está o problema. O script é simples, não é tão performático porque os sistemas que são consultados também não são rápidos, mas acredito que deve ajudar mesmo assim ou até mesmo servir como prova de conceito para quem está estudando Redes.<br />
<br />
== O script lgview.py ==<br />
Para utilizar o script você precisará do módulo '''netmiko''' instalado. Esse módulo te permite fazer conexões, via python, em diversos tipos de devices. Como utilizaremos o Debian aqui, é bem simples de instalar:<br />
# apt install python3-netmiko<br />
Instalado o '''netmiko''' basta criar o script abaixo. Este script é a primeira versão, pode conter bugs mas sinta-se convidado a ajudar a melhorá-lo. Pode me contactar para isso. <br />
#!/usr/bin/env python3<br />
<nowiki>'''</nowiki><br />
lgview is free software; you can redistribute it and/or modify<br />
it under the terms of the GNU General Public License as published by<br />
the Free Software Foundation; either version 2 of the License, or<br />
(at your option) any later version.<br />
<nowiki>#</nowiki><br />
This program is distributed in the hope that it will be useful,<br />
but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
GNU General Public License for more details.<br />
<nowiki>#</nowiki><br />
You should have received a copy of the GNU General Public License<br />
along with this program; if not, write to the Free Software<br />
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
<br />
Precisa do pacote: python3-netmiko<br />
<nowiki>'''</nowiki><br />
import os<br />
import sys<br />
import re<br />
from netmiko import ConnectHandler<br />
<br />
__author__ = 'Marcelo Gondim'<br />
__version__= 1.0<br />
__datebegin__= "25/06/2023"<br />
<nowiki>#########################################################################</nowiki><br />
routeviews = {<br />
<nowiki> </nowiki> "device_type": "cisco_xe",<br />
<nowiki> </nowiki> "host": "route-views.routeviews.org",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": ""<br />
}<br />
<br />
ix_sp = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.sp.ptt.br",<br />
}<br />
<br />
ix_rj = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.rj.ix.br",<br />
}<br />
<br />
ix_ce = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.ce.ix.br",<br />
}<br />
<br />
at_t = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "route-server.ip.att.net",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": "rviews"<br />
}<br />
<br />
internexa = {<br />
<nowiki> </nowiki> "device_type": "juniper_junos",<br />
<nowiki> </nowiki> "host": "177.84.161.226",<br />
<nowiki> </nowiki> "username": "bgp_view",<br />
<nowiki> </nowiki> "password": "bgp_view"<br />
}<br />
<br />
algar = {<br />
<nowiki> </nowiki> "device_type": "juniper_junos",<br />
<nowiki> </nowiki> "host": "201.48.0.2",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": "rviews"<br />
}<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
try:<br />
<nowiki> </nowiki> prefixo = sys.argv[1]<br />
<nowiki> </nowiki> asn_mitigacao = sys.argv[2]<br />
<nowiki> </nowiki> asn_prefixo = sys.argv[3]<br />
except:<br />
<nowiki> </nowiki> print("Parametros faltando! Precisa passar o prefixo IPv4 /24 mitigado, o ASN mitigador e o ASN do prefixo mitigado.")<br />
<nowiki> </nowiki> print("Ex.: ./lgview.py 192.168.0.0/24 65000 65001")<br />
<nowiki> </nowiki> exit(0)<br />
<br />
<br />
def grep(texto, dado):<br />
<nowiki> </nowiki> linhas = texto.split('\n')<br />
<nowiki> </nowiki> lista = []<br />
<br />
<nowiki> </nowiki> for linha in linhas:<br />
<nowiki> </nowiki> if re.search(dado, linha):<br />
<nowiki> </nowiki> if "community" not in linha.lower():<br />
<nowiki> </nowiki> lista.append(linha)<br />
<br />
<nowiki> </nowiki> return lista<br />
<br />
os.system('cls' if os.name == 'nt' else 'clear')<br />
titulo = "LGVIEW - Lista prefixos nos Looking Glass para troubleshooting de Mitigacao DDoS - %s - v%s - %s" % (__author__, __version__,__datebegin__)<br />
print("#"*126)<br />
print(" %s" %(titulo))<br />
print("#"*126)<br />
print("Verificando Prefixo: " + prefixo + "\n")<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: route-views.routeviews.org")<br />
consulta = ConnectHandler(**routeviews)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-SP")<br />
consulta = ConnectHandler(**ix_sp)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-RJ")<br />
consulta = ConnectHandler(**ix_rj)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-CE")<br />
consulta = ConnectHandler(**ix_ce)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: AT&T")<br />
consulta = ConnectHandler(**at_t)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: Internexa")<br />
consulta = ConnectHandler(**internexa)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: Algar")<br />
consulta = ConnectHandler(**algar)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
Para executar é bem simples, passe como parâmetros: o '''prefixo IPv4 /24''' que quer fazer a busca, o '''ASN da empresa mitigadora''' e o '''ASN do seu prefixo'''. Abaixo um exemplo de chamada:<br />
# ./lgview.py 198.51.100.0/24 65200 65001<br />
[[Arquivo:Lgview.png|nenhum|commoldura]]As linhas em '''vermelho''' merecem sua atenção porque indicam um provável vazamento do prefixo. Em algumas situações pode não ser um vazamento, mas precisa ser checado. Precisamos garantir que o prefixo /24 atacado, esteja passando pela nuvem de mitigação DDoS.<br />
<br />
== Conclusão ==<br />
Espero que além de útil, o artigo possa ter passado o quão é importante essa validação.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Vazamento_de_prefixos_na_mitiga%C3%A7%C3%A3o_DDoS&diff=3498Vazamento de prefixos na mitigação DDoS2023-06-26T03:16:26Z<p>Gondim: </p>
<hr />
<div>__TOC__<br />
== Objetivo ==<br />
Ajudar no troubleshooting de um dos principais problemas relacionados com o não funcionamento de uma Mitigação DDoS, o '''vazamento dos prefixos /24'''. Quando se tem contratado uma nuvem de mitigação DDoS, precisamos garantir que o '''prefixo /24 atacado''' passe pelo '''Scrubbing Center''' da fornecedora de mitigação DDoS. Se o tráfego estiver vazando por outros trânsitos IP, IX, etc ou se o prefixo IP não tiver sendo devidamente anunciado, sua mitigação falhará e o ataque poderá vir por outros caminhos. Embora existam ataques em IPv6, o IPv4 ainda é o principal alvo dos ataques DDoS e por isso esse artigo está focado no IPv4. Provedores que já utilizam IPv6 se beneficiam de '''menor consumo de clean pipe''', '''diminuição de efeitos colaterais causados pela mitigação''' e '''economia em CGNAT'''.<br />
<br />
A maneira mais simples de checarmos os vazamos de '''prefixos /24''', é usando os diversos '''Looking Glass''' que encontramos pela Internet'''.''' Neles podemos ver se os prefixos estão realmente passando por onde devem. Pensando em ajudar nessa tarefa criei um script em python que checa em alguns '''Looking Glass''', como está o anúncio do prefixo e te avisar onde está o problema. O script é simples, não é tão performático porque os sistemas que são consultados também não são rápidos, mas acredito que deve ajudar mesmo assim ou até mesmo servir como prova de conceito para quem está estudando Redes.<br />
<br />
== O script lgview.py ==<br />
Para utilizar o script você precisará do módulo '''netmiko''' instalado. Esse módulo te permite fazer conexões, via python, em diversos tipos de devices. Como utilizaremos o Debian aqui, é bem simples de instalar:<br />
# apt install python3-netmiko<br />
Instalado o '''netmiko''' basta criar o script abaixo. Este script é a primeira versão, pode conter bugs mas sinta-se convidado a ajudar a melhorá-lo. Pode me contactar para isso. <br />
#!/usr/bin/env python3<br />
<nowiki>'''</nowiki><br />
lgview is free software; you can redistribute it and/or modify<br />
it under the terms of the GNU General Public License as published by<br />
the Free Software Foundation; either version 2 of the License, or<br />
(at your option) any later version.<br />
<nowiki>#</nowiki><br />
This program is distributed in the hope that it will be useful,<br />
but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
GNU General Public License for more details.<br />
<nowiki>#</nowiki><br />
You should have received a copy of the GNU General Public License<br />
along with this program; if not, write to the Free Software<br />
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
<br />
Precisa do pacote: python3-netmiko<br />
<nowiki>'''</nowiki><br />
import os<br />
import sys<br />
import re<br />
from netmiko import ConnectHandler<br />
<br />
__author__ = 'Marcelo Gondim'<br />
__version__= 1.0<br />
__datebegin__= "25/06/2023"<br />
<nowiki>#########################################################################</nowiki><br />
routeviews = {<br />
<nowiki> </nowiki> "device_type": "cisco_xe",<br />
<nowiki> </nowiki> "host": "route-views.routeviews.org",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": ""<br />
}<br />
<br />
ix_sp = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.sp.ptt.br",<br />
}<br />
<br />
ix_rj = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.rj.ix.br",<br />
}<br />
<br />
ix_ce = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.ce.ix.br",<br />
}<br />
<br />
at_t = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "route-server.ip.att.net",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": "rviews"<br />
}<br />
<br />
internexa = {<br />
<nowiki> </nowiki> "device_type": "juniper_junos",<br />
<nowiki> </nowiki> "host": "177.84.161.226",<br />
<nowiki> </nowiki> "username": "bgp_view",<br />
<nowiki> </nowiki> "password": "bgp_view"<br />
}<br />
<br />
algar = {<br />
<nowiki> </nowiki> "device_type": "juniper_junos",<br />
<nowiki> </nowiki> "host": "201.48.0.2",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": "rviews"<br />
}<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
try:<br />
<nowiki> </nowiki> prefixo = sys.argv[1]<br />
<nowiki> </nowiki> asn_mitigacao = sys.argv[2]<br />
<nowiki> </nowiki> asn_prefixo = sys.argv[3]<br />
except:<br />
<nowiki> </nowiki> print("Parametros faltando! Precisa passar o prefixo IPv4 /24 mitigado, o ASN mitigador e o ASN do prefixo mitigado.")<br />
<nowiki> </nowiki> print("Ex.: ./lgview.py 192.168.0.0/24 65000 65001")<br />
<nowiki> </nowiki> exit(0)<br />
<br />
<br />
def grep(texto, dado):<br />
<nowiki> </nowiki> linhas = texto.split('\n')<br />
<nowiki> </nowiki> lista = []<br />
<br />
<nowiki> </nowiki> for linha in linhas:<br />
<nowiki> </nowiki> if re.search(dado, linha):<br />
<nowiki> </nowiki> if "community" not in linha.lower():<br />
<nowiki> </nowiki> lista.append(linha)<br />
<br />
<nowiki> </nowiki> return lista<br />
<br />
os.system('cls' if os.name == 'nt' else 'clear')<br />
titulo = "LGVIEW - Lista prefixos nos Looking Glass para troubleshooting de Mitigacao DDoS - %s - v%s - %s" % (__author__, __version__,__datebegin__)<br />
print("#"*126)<br />
print(" %s" %(titulo))<br />
print("#"*126)<br />
print("Verificando Prefixo: " + prefixo + "\n")<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: route-views.routeviews.org")<br />
consulta = ConnectHandler(**routeviews)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-SP")<br />
consulta = ConnectHandler(**ix_sp)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-RJ")<br />
consulta = ConnectHandler(**ix_rj)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-CE")<br />
consulta = ConnectHandler(**ix_ce)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: AT&T")<br />
consulta = ConnectHandler(**at_t)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: Internexa")<br />
consulta = ConnectHandler(**internexa)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: Algar")<br />
consulta = ConnectHandler(**algar)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
Para executar é bem simples, passe como parâmetros: o '''prefixo IPv4 /24''' que quer fazer a busca, o '''ASN da empresa mitigadora''' e o '''ASN do seu prefixo'''. Abaixo um exemplo de chamada:<br />
# ./lgview.py 198.51.100.0/24 65200 65001<br />
[[Arquivo:Lgview.png|nenhum|commoldura]]As linhas em '''vermelho''' merecem sua atenção porque indicam um provável vazamento do prefixo. Em algumas situações pode não ser um vazamento, mas precisa ser checado. Precisamos garantir que o prefixo /24 esteja passando pela nuvem de mitigação DDoS.<br />
<br />
== Conclusão ==<br />
Espero que além de útil, o artigo possa ter passado o quão é importante essa validação.<br />
<br />
Essa documentação foi útil? Compartilhe, divulgue e ajude outras pessoas.<br />
<br />
Autor: [[Usuário:Gondim|Marcelo Gondim]]<br />
[[Categoria:Infraestrutura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Vazamento_de_prefixos_na_mitiga%C3%A7%C3%A3o_DDoS&diff=3497Vazamento de prefixos na mitigação DDoS2023-06-26T03:05:30Z<p>Gondim: Criou página com '== Objetivo == Ajudar no troubleshooting de um dos principais problemas relacionados com o não funcionamento de uma Mitigação DDoS, o '''vazamento dos prefixos /24'''. Quan...'</p>
<hr />
<div>== Objetivo ==<br />
Ajudar no troubleshooting de um dos principais problemas relacionados com o não funcionamento de uma Mitigação DDoS, o '''vazamento dos prefixos /24'''. Quando se tem contratado uma nuvem de mitigação DDoS, precisamos garantir que o '''prefixo /24 atacado''' passe pelo '''Scrubbing Center''' da fornecedora de mitigação DDoS. Se o tráfego estiver vazando por outros trânsitos IP, IX, etc ou se o prefixo IP não tiver sendo devidamente anunciado, sua mitigação falhará e o ataque poderá vir por outros caminhos. Embora existam ataques em IPv6, o IPv4 ainda é o principal alvo dos ataques DDoS e por isso esse artigo está focado no IPv4. Provedores que já utilizam IPv6 se beneficiam de '''menor consumo de clean pipe''', '''diminuição de efeitos colaterais causados pela mitigação''' e '''economia em CGNAT'''.<br />
<br />
A maneira mais simples de checarmos os vazamos de '''prefixos /24''', é usando os diversos '''Looking Glass''' que encontramos pela Internet'''.''' Neles podemos ver se os prefixos estão realmente passando por onde devem. Pensando em ajudar nessa tarefa criei um script em python que checa em alguns '''Looking Glass''', como está o anúncio do prefixo e te avisar onde está o problema. O script é simples, não é tão performático porque os sistemas que são consultados também não são rápidos, mas acredito que deve ajudar mesmo assim ou até mesmo servir como prova de conceito para quem está estudando Redes.<br />
<br />
== O script lgview.py ==<br />
Para utilizar o script você precisará do módulo '''netmiko''' instalado. Esse módulo te permite fazer conexões, via python, em diversos tipos de devices. Como utilizaremos o Debian aqui, é bem simples de instalar:<br />
# apt install python3-netmiko<br />
Instalado o '''netmiko''' basta criar o script abaixo. Este script é a primeira versão, pode conter bugs mas sinta-se convidado a ajudar a melhorá-lo. Pode me contactar para isso. <br />
#!/usr/bin/env python3<br />
<nowiki>'''</nowiki><br />
lgview is free software; you can redistribute it and/or modify<br />
it under the terms of the GNU General Public License as published by<br />
the Free Software Foundation; either version 2 of the License, or<br />
(at your option) any later version.<br />
<nowiki>#</nowiki><br />
This program is distributed in the hope that it will be useful,<br />
but WITHOUT ANY WARRANTY; without even the implied warranty of<br />
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />
GNU General Public License for more details.<br />
<nowiki>#</nowiki><br />
You should have received a copy of the GNU General Public License<br />
along with this program; if not, write to the Free Software<br />
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
<br />
Precisa do pacote: python3-netmiko<br />
<nowiki>'''</nowiki><br />
import os<br />
import sys<br />
import re<br />
from netmiko import ConnectHandler<br />
<br />
__author__ = 'Marcelo Gondim'<br />
__version__= 1.0<br />
__datebegin__= "25/06/2023"<br />
<nowiki>#########################################################################</nowiki><br />
routeviews = {<br />
<nowiki> </nowiki> "device_type": "cisco_xe",<br />
<nowiki> </nowiki> "host": "route-views.routeviews.org",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": ""<br />
}<br />
<br />
ix_sp = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.sp.ptt.br",<br />
}<br />
<br />
ix_rj = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.rj.ix.br",<br />
}<br />
<br />
ix_ce = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "lg.ce.ix.br",<br />
}<br />
<br />
at_t = {<br />
<nowiki> </nowiki> "device_type": "cisco_ios_telnet",<br />
<nowiki> </nowiki> "host": "route-server.ip.att.net",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": "rviews"<br />
}<br />
<br />
internexa = {<br />
<nowiki> </nowiki> "device_type": "juniper_junos",<br />
<nowiki> </nowiki> "host": "177.84.161.226",<br />
<nowiki> </nowiki> "username": "bgp_view",<br />
<nowiki> </nowiki> "password": "bgp_view"<br />
}<br />
<br />
algar = {<br />
<nowiki> </nowiki> "device_type": "juniper_junos",<br />
<nowiki> </nowiki> "host": "201.48.0.2",<br />
<nowiki> </nowiki> "username": "rviews",<br />
<nowiki> </nowiki> "password": "rviews"<br />
}<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
try:<br />
<nowiki> </nowiki> prefixo = sys.argv[1]<br />
<nowiki> </nowiki> asn_mitigacao = sys.argv[2]<br />
<nowiki> </nowiki> asn_prefixo = sys.argv[3]<br />
except:<br />
<nowiki> </nowiki> print("Parametros faltando! Precisa passar o prefixo IPv4 /24 mitigado, o ASN mitigador e o ASN do prefixo mitigado.")<br />
<nowiki> </nowiki> print("Ex.: ./lgview.py 192.168.0.0/24 65000 65001")<br />
<nowiki> </nowiki> exit(0)<br />
<br />
<br />
def grep(texto, dado):<br />
<nowiki> </nowiki> linhas = texto.split('\n')<br />
<nowiki> </nowiki> lista = []<br />
<br />
<nowiki> </nowiki> for linha in linhas:<br />
<nowiki> </nowiki> if re.search(dado, linha):<br />
<nowiki> </nowiki> if "community" not in linha.lower():<br />
<nowiki> </nowiki> lista.append(linha)<br />
<br />
<nowiki> </nowiki> return lista<br />
<br />
os.system('cls' if os.name == 'nt' else 'clear')<br />
titulo = "LGVIEW - Lista prefixos nos Looking Glass para troubleshooting de Mitigacao DDoS - %s - v%s - %s" % (__author__, __version__,__datebegin__)<br />
print("#"*126)<br />
print(" %s" %(titulo))<br />
print("#"*126)<br />
print("Verificando Prefixo: " + prefixo + "\n")<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: route-views.routeviews.org")<br />
consulta = ConnectHandler(**routeviews)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-SP")<br />
consulta = ConnectHandler(**ix_sp)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-RJ")<br />
consulta = ConnectHandler(**ix_rj)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: IX-CE")<br />
consulta = ConnectHandler(**ix_ce)<br />
consulta.send_command('terminal length 0')<br />
resultado = consulta.send_command('show ip bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: AT&T")<br />
consulta = ConnectHandler(**at_t)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: Internexa")<br />
consulta = ConnectHandler(**internexa)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
<br />
print("Checando LG: Algar")<br />
consulta = ConnectHandler(**algar)<br />
consulta.send_command('set cli screen-length 0')<br />
resultado = consulta.send_command('show route protocol bgp ' + prefixo)<br />
<br />
busca = grep(resultado, asn_prefixo)<br />
<br />
for elemento in busca:<br />
<nowiki> </nowiki> if asn_mitigacao not in elemento:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[91m' + elemento + '\033[0m')<br />
<nowiki> </nowiki> else:<br />
<nowiki> </nowiki> print('AS-PATH: ' + '\033[92m' + elemento + '\033[0m')<br />
<br />
<nowiki>#########################################################################</nowiki><br />
Para executar é bem simples, passe como parâmetros: o '''prefixo IPv4 /24''' que quer fazer a busca, o '''ASN da empresa mitigadora''' e o '''ASN do seu prefixo'''. Abaixo um exemplo de chamada:<br />
# ./lgview.py 198.51.100.0/24 65200 65001<br />
[[Arquivo:Lgview.png|nenhum|commoldura]]</div>Gondimhttps://wiki.brasilpeeringforum.org/index.php?title=Arquivo:Lgview.png&diff=3496Arquivo:Lgview.png2023-06-26T03:03:56Z<p>Gondim: </p>
<hr />
<div>lgview</div>Gondim