DNS recursivo com Unbound para provedor: instalação e DNSSEC

Todo acesso do cliente começa com uma consulta de nome. Antes do vídeo, do jogo ou do aplicativo do banco, alguém precisa transformar o nome em endereço. Quem faz esse trabalho é o DNS recursivo, e no provedor ele pode ser seu ou de terceiros. Este guia monta um DNS recursivo próprio com Unbound no Debian, pronto para atender os clientes da rede, com validação DNSSEC, IPv6 e os ajustes de cache que fazem diferença com milhares de assinantes.
O Unbound é o resolvedor da NLnet Labs. Ele só faz recursão e cache, não hospeda zonas autoritativas, e por isso a configuração fica curta. Os exemplos usam o endereço de serviço 198.51.100.53 e 2001:db8:53::53, clientes em CGNAT na faixa 100.64.0.0/10, um bloco público 203.0.113.0/24 e clientes IPv6 em 2001:db8:100::/40. Troque pelos seus endereços.
Resposta rápida: o mínimo para um DNS recursivo seguro
- Instale com
apt install unbound. O pacote do Debian já liga a validação DNSSEC e ounbound-control. - Escute só nos endereços de serviço, com uma linha
interface:para cada um. - Recuse todo mundo com
access-control: 0.0.0.0/0 refusee libere apenas os blocos dos clientes. Resolvedor aberto vira arma de ataque por amplificação. - Ajuste threads e cache ao tamanho do servidor e ligue
prefetch. - Teste com
dig +dnssec: a flagadna resposta prova que a validação funciona.
Por que ter um DNS recursivo próprio no provedor
Muito provedor pequeno entrega aos clientes o resolvedor público de uma grande empresa e esquece o assunto. Funciona, mas cada consulta sai da rede, cruza o trânsito e volta. Com um DNS recursivo local, a resposta mais pedida (os domínios das plataformas de vídeo, das redes sociais e das lojas de aplicativos) já está em cache e volta em um ou dois milissegundos.
Há também o efeito sobre as CDNs. O servidor autoritativo de uma CDN decide qual nó entregar olhando o endereço de quem perguntou, que é o resolvedor. Com o recursivo dentro da sua rede, a resposta tende a apontar para o cache mais próximo de você, inclusive os que ficam no IX ou dentro do próprio provedor.
| Ponto | DNS recursivo próprio | Resolvedor público |
|---|---|---|
| Latência | Consulta respondida dentro da sua rede, em poucos milissegundos quando está em cache | Consulta atravessa o trânsito até o resolvedor público |
| CDN e caches | O autoritativo da CDN enxerga o seu endereço de saída e indica o nó mais perto da sua rede | A CDN pode indicar um nó que não é o melhor para o cliente |
| Controle | Você vê os logs, mede o volume e aplica bloqueios exigidos por ordem judicial | Nenhuma visibilidade nem controle |
| Dependência | Funciona mesmo com o trânsito instável, para tudo que já está em cache | Se o caminho até o resolvedor cair, o cliente fica sem nome nenhum |
| Custo | Um servidor, monitoramento e atualização | Nenhum custo direto |
O argumento contra é operacional: o servidor passa a ser seu problema. Se ele travar, todos os clientes ficam sem internet, mesmo com a rota funcionando. Por isso o mínimo são dois servidores em máquinas diferentes, entregues aos clientes como primário e secundário pelo PPPoE, pelo DHCP ou pelo RA do IPv6.
Como o DNS recursivo resolve um nome
O cliente faz uma única pergunta, com a flag RD (recursão desejada), e espera a resposta pronta. O recursivo faz o resto. Sem nada em cache, ele pergunta à raiz quem cuida do .br, pergunta aos servidores do .br quem cuida do domínio e, por fim, pergunta ao servidor autoritativo do domínio pelo registro pedido.

Cada resposta guardada vale pelo TTL definido pelo dono do domínio. Os servidores do .br e da raiz quase nunca precisam ser consultados de novo, porque esses registros têm TTL longo. Na prática, a maior parte do trabalho é a última etapa e a validação das assinaturas.
Com DNSSEC, cada nível assina os próprios registros e publica a impressão da chave do nível de baixo (o registro DS). O recursivo parte de uma chave da raiz em que confia, a âncora de confiança, e confere a cadeia até o domínio. Se uma assinatura não fecha, a resposta é tratada como falsa e o cliente recebe SERVFAIL.
Instalar o Unbound no Debian
Use uma máquina dedicada ou uma VM com dois núcleos e 2 GB de RAM para começar. O pacote dnsutils traz o dig, que vamos usar nos testes.
apt update apt install unbound dnsutils
O serviço sobe sozinho escutando só em localhost. Antes de mexer, veja o que o pacote do Debian já deixou pronto:
ls /etc/unbound/unbound.conf.d/
remote-control.conf root-auto-trust-anchor-file.conf
O unbound.conf principal só inclui os arquivos de unbound.conf.d. O remote-control.conf habilita o unbound-control por um socket local, sem precisar gerar certificados. O outro arquivo é o que mais interessa:
server:
# The following line will configure unbound to perform cryptographic
# DNSSEC validation using the root trust anchor.
auto-trust-anchor-file: "/var/lib/unbound/root.key"Ou seja, a validação DNSSEC já vem ligada. Na partida do serviço, um script do pacote copia a chave da raiz para /var/lib/unbound/root.key, e o Unbound mantém esse arquivo atualizado sozinho, seguindo a RFC 5011 quando a raiz troca de chave.
A chave vem do pacote dns-root-data, que o apt instala junto por ser recomendado. Se a instalação foi feita sem os pacotes recomendados e o root.key não existir, crie o arquivo com sudo -u unbound unbound-anchor -a /var/lib/unbound/root.key antes de reiniciar o serviço.
unbound-checkconf acusar erro e o serviço não subir.Configuração do DNS recursivo para os clientes
Crie o arquivo /etc/unbound/unbound.conf.d/provedor.conf com o conteúdo abaixo. Tudo fica dentro de server:, com quatro espaços de recuo.
server:
interface: 127.0.0.1
interface: ::1
interface: 198.51.100.53
interface: 2001:db8:53::53
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
access-control: 0.0.0.0/0 refuse
access-control: ::/0 refuse
access-control: 127.0.0.0/8 allow
access-control: ::1/128 allow
access-control: 100.64.0.0/10 allow
access-control: 203.0.113.0/24 allow
access-control: 2001:db8:100::/40 allow
num-threads: 2
so-reuseport: yes
so-rcvbuf: 4m
so-sndbuf: 4m
msg-cache-size: 128m
rrset-cache-size: 256m
prefetch: yes
prefetch-key: yes
serve-expired: yes
serve-expired-ttl: 86400
hide-identity: yes
hide-version: yes
qname-minimisation: yes
harden-dnssec-stripped: yes
unwanted-reply-threshold: 10000000
val-log-level: 1Interfaces: escute nos endereços de serviço
Sem nenhuma linha interface:, o Unbound escuta só em localhost. Daria para usar 0.0.0.0 e ::0, mas num servidor com vários endereços a resposta pode sair por um IP diferente daquele que o cliente consultou, e o cliente descarta. Listar cada endereço de serviço evita esse problema. Endereço novo exige restart, porque o reload não reabre os sockets.
Access-control: o recursivo não pode ser aberto
As duas primeiras regras recusam qualquer origem. As seguintes liberam localhost e os blocos dos clientes. Vale a regra do prefixo mais específico, então a ordem não muda o resultado, mas deixar o refuse no topo deixa a intenção clara para quem ler depois.
A ação refuse devolve REFUSED; deny simplesmente descarta. Para clientes que configuraram o seu DNS por engano, o REFUSED avisa na hora em vez de deixar a consulta esperando o timeout. Lembre de incluir todos os blocos: o pool do CGNAT, os IPs públicos fixos dos clientes corporativos e os prefixos IPv6 entregues por DHCPv6-PD ou SLAAC.
Desempenho: threads, buffers e cache
num-threads: um por núcleo de CPU. Os parâmetros*-slabsficam no automático, que escolhe uma potência de 2 próxima do número de threads.so-reuseport: abre um socket por thread no Linux e distribui melhor as consultas UDP. Já é o padrão; fica explícito para documentar a escolha.so-rcvbufeso-sndbuf: buffers maiores seguram picos de consultas sem perder pacotes. O kernel limita o tamanho, por isso o sysctl logo abaixo.msg-cache-sizeerrset-cache-size: a documentação recomenda o cache de RRsets com cerca do dobro do cache de mensagens. Por causa da sobra de alocação, o processo chega a usar de duas a duas vezes e meia a soma dos dois. Com 128 MB e 256 MB, conte com cerca de 1 GB de RAM para o Unbound; numa máquina com 4 GB, dá para dobrar os dois valores.
cat > /etc/sysctl.d/90-unbound.conf <<'EOF' net.core.rmem_max = 8388608 net.core.wmem_max = 8388608 EOF sysctl --system
Prefetch e respostas vencidas
Com prefetch: yes, quando um registro popular é consultado nos últimos 10% do TTL, o Unbound busca a versão nova antes de vencer. O custo é cerca de 10% a mais de tráfego, e o ganho é que os nomes mais pedidos nunca saem do cache. O prefetch-key busca as chaves DNSKEY assim que encontra o registro DS, em vez de esperar a etapa de validação, o que reduz a latência da primeira consulta a um domínio assinado.
O serve-expired responde com o dado vencido quando a resolução falha ou demora, por até serve-expired-ttl segundos (um dia, no exemplo, valor sugerido pela RFC 8767). Se o autoritativo de um site cair, quem já tinha o nome em cache continua chegando ao endereço antigo.
Privacidade e proteção
hide-identityehide-version: recusam as consultas que revelam o nome do servidor e a versão do Unbound.qname-minimisation: o recursivo manda para a raiz e para o TLD só a parte do nome que cada um precisa ver. Já é o padrão; deixá-lo explícito documenta a escolha.harden-dnssec-stripped: se a zona deveria ser assinada e a assinatura sumiu no caminho, a resposta é tratada como falsa.unwanted-reply-threshold: conta respostas que ninguém pediu, sinal de tentativa de envenenamento. Ao passar do limite, o cache é limpo e um aviso vai para o log. O valor de 10 milhões é o sugerido pela documentação.val-log-level: 1: registra no log cada consulta que falhou na validação DNSSEC.ip-ratelimit: limita as consultas por segundo de cada endereço de origem (padrão 0, desligado). Ajuda contra cliente infectado que dispara consultas, mas cuidado com clientes corporativos que saem por um único IP público: comece com valor alto e acompanhe o log antes de apertar.
Validar e aplicar a configuração
Nunca reinicie o serviço sem conferir a sintaxe. Num DNS recursivo que atende a rede inteira, um erro de digitação derruba a navegação de todos.
unbound-checkconf unbound-checkconf -o auto-trust-anchor-file ls -l /var/lib/unbound/root.key systemctl restart unbound systemctl status unbound --no-pager ss -lntup 'sport = :53'
O primeiro comando valida a configuração inteira, incluindo os arquivos do pacote. O segundo mostra o valor efetivo da âncora de confiança, e o ls confirma que o arquivo existe. Depois do restart, o ss deve listar o Unbound escutando na porta 53, em UDP e TCP, em cada endereço de serviço. Saída esperada dos dois primeiros:
unbound-checkconf: no errors in /etc/unbound/unbound.conf /var/lib/unbound/root.key
DNSSEC e IPv6 no DNS recursivo
Como o DNSSEC aparece para o cliente
O cliente comum não valida nada: ele confia no recursivo. Quando a validação dá certo, o Unbound liga a flag ad (dados autenticados) na resposta. Quando falha, devolve SERVFAIL, e o site simplesmente não abre. Isso protege contra resposta forjada, mas também quer dizer que um erro de assinatura do dono do domínio vira chamado no seu suporte.
Antes de desligar a validação para “resolver” um site que não abre, confira com a flag +cd do dig, que pede a resposta sem validar. Se com +cd o nome resolve e sem ela dá SERVFAIL, o problema é a assinatura do domínio, e o certo é avisar o responsável por ele.
IPv6 nos dois sentidos
No DNS recursivo, o IPv6 aparece em dois lugares. Do lado do cliente, o recursivo escuta em 2001:db8:53::53 e libera o prefixo dos assinantes no access-control. Do lado da internet, com do-ip6: yes, o Unbound também consulta os autoritativos por IPv6.
Se o servidor tem endereço IPv6 mas não tem rota IPv6 para fora, as consultas por IPv6 vão esperar timeout antes de cair para o IPv4, e a resolução fica lenta sem erro aparente. Confirme que o servidor alcança a internet em IPv6 antes de ligar a opção. Se precisar de uma revisão, veja o que é IPv6 e como ele funciona.
Testes com dig: flag ad, SERVFAIL e REFUSED
Rode os testes de uma máquina que está num bloco liberado, por exemplo um roteador de cliente de laboratório.
dig @198.51.100.53 registro.br A +dnssec dig @2001:db8:53::53 registro.br AAAA dig @198.51.100.53 dnssec-failed.org A dig @198.51.100.53 dnssec-failed.org A +cd
Linha a linha:
- A primeira consulta pede um domínio assinado com DNSSEC. A resposta precisa trazer a flag
ad. - A segunda faz a mesma pergunta pelo endereço IPv6 do serviço, pedindo o registro AAAA.
- O
dnssec-failed.orgé um domínio de teste mantido com a assinatura quebrada de propósito. Um recursivo que valida precisa devolver SERVFAIL. - A última repete a consulta com
+cd. Agora o nome resolve, o que prova que o SERVFAIL anterior veio da validação, e não de falha de rede.
Cabeçalho esperado no primeiro teste:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21877 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
E no teste do domínio com assinatura quebrada:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 40112 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
Falta o teste mais importante para a segurança: consultar a partir de um endereço que não está liberado, como uma VPS fora da sua rede. A resposta precisa ser REFUSED. Se vier a resposta normal, o seu DNS recursivo está aberto para a internet.
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 5310 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
Operação no dia a dia
unbound-control status unbound-control stats_noreset | grep total.num unbound-control flush_zone exemplo.com.br journalctl -u unbound --since "1 hour ago"
unbound-control statusmostra versão, threads, módulos (validator e iterator) e há quanto tempo o processo está no ar.stats_noresetlê os contadores sem zerá-los. Ototal.num.queriese ototal.num.cachehitsdão a taxa de acerto do cache. Use a variante sem reset quando outra ferramenta, como o Zabbix, também coleta esses números.flush_zoneapaga do cache tudo que está abaixo de um domínio. Útil quando um cliente mudou o apontamento do site e não quer esperar o TTL.- O
journalctlmostra os erros de validação registrados peloval-log-level.
Para entregar o DNS recursivo aos clientes, coloque os dois servidores no profile do concentrador. No Mikrotik é o campo dns-server do profile PPP, como mostrado no guia de PPPoE server no Mikrotik RouterOS v7. Em redes com o que é CGNAT, o DNS local tem uma vantagem extra: a consulta não passa pelo NAT, o que economiza portas da faixa de cada cliente.
Erros comuns ao montar o DNS recursivo
- Resolvedor aberto. Um
access-control: 0.0.0.0/0 allowesquecido de um teste transforma o servidor em refletor de ataques DDoS. Faça o teste de fora da rede depois de qualquer mudança. - Âncora de confiança duplicada. Copiar o
auto-trust-anchor-filede um tutorial genérico para dentro do arquivo do provedor quebra a configuração no Debian. - Relógio errado. As assinaturas DNSSEC têm data de início e de fim. Um servidor com a hora errada recusa respostas válidas e tudo dá SERVFAIL. Mantenha o NTP funcionando.
- Bloco de cliente faltando. Pool novo do CGNAT ou prefixo IPv6 novo sem a linha correspondente no
access-controlgera chamados de “internet sem DNS” só para uma parte dos clientes. - Um servidor só. Recursivo único é ponto único de falha. Dois servidores, de preferência em racks ou POPs diferentes.
- Firewall barrando TCP 53. Respostas grandes, comuns com DNSSEC, caem para TCP. Libere UDP e TCP na porta 53 para os clientes.
Perguntas frequentes
Qual a diferença entre DNS recursivo e DNS autoritativo?
O autoritativo guarda as zonas e responde só pelos domínios que hospeda. O recursivo não hospeda nada: ele busca a resposta nos autoritativos, valida e guarda em cache para os clientes. O Unbound faz só o papel de recursivo.
Quanto de hardware o Unbound precisa?
Pouco. Um DNS recursivo com Unbound numa VM com dois núcleos e 2 GB de RAM atende com folga um provedor de alguns milhares de clientes. Ajuste o num-threads ao número de núcleos e o cache à memória disponível, e acompanhe a taxa de acerto do cache para saber se vale crescer.
Por que o site abre no resolvedor público e não no meu?
A causa mais comum é DNSSEC quebrado no domínio. Alguns resolvedores públicos criam exceções temporárias para domínios conhecidos com assinatura errada, e o seu servidor não tem essas exceções. Teste com dig +cd: se resolve com a flag e dá SERVFAIL sem ela, a assinatura do domínio está errada.
Devo usar forward para um resolvedor público?
Para um provedor, não costuma valer a pena. O forward devolve a dependência externa e faz as CDNs enxergarem o endereço do resolvedor público, e não o seu. Recursão completa é o que dá o ganho de latência e de localização de cache.
Preciso abrir a porta 53 para a internet?
Não. A porta 53 do recursivo só precisa receber consultas dos clientes. As respostas dos autoritativos chegam em portas altas aleatórias, como retorno das conexões que o próprio servidor abriu. Num firewall com estado, basta permitir o tráfego de saída e o de retorno.
Conclusão
Um DNS recursivo próprio com Unbound cabe em poucas linhas no Debian: escutar nos endereços certos, recusar quem não é cliente, dimensionar o cache e deixar a validação DNSSEC que o pacote já traz ligada. O resultado é menos latência, CDNs entregando conteúdo de mais perto e controle sobre um serviço do qual todos os clientes dependem.
Monte dois servidores, teste de fora da rede para garantir que não estão abertos e acompanhe os contadores do unbound-control. Com isso o recursivo deixa de ser uma dependência externa e passa a ser parte da sua rede.
Leitura recomendada: Servidor DNS recursivo com IPv6 e DNSSEC e Porque usar um DNS local, na wiki do Brasil Peering Forum (CC BY-NC 4.0).

[…] o anúncio quando o Unbound para. A instalação e o ajuste do recursivo em si estão no post sobre DNS recursivo com Unbound para provedor. Os endereços e ASNs são de […]