DNS hyperlocal no Unbound: zona raiz local com RFC 8806

DNS hyperlocal é o nome que se usa no Brasil para manter uma cópia da zona raiz do DNS dentro do próprio recursivo. Em vez de perguntar aos servidores raiz quem responde pelo .br ou pelo .com, o Unbound consulta a cópia que ele mesmo baixou e validou. A técnica está descrita na RFC 8806, que substituiu a RFC 7706.
Este post parte do recursivo montado em DNS recursivo com Unbound para provedor: Debian, Unbound com DNSSEC e access-control fechado. Aqui entra só o que muda para ter o DNS hyperlocal: um arquivo de configuração, os testes com dig, como medir se valeu a pena e como vigiar a cópia para ela não envelhecer. Tudo foi montado a partir da documentação do Unbound e da RFC, com endereços de documentação para o servidor.
Resposta rápida
- Crie um
auth-zonepara"."com os servidores que aceitam AXFR da raiz comoprimary. - Use
for-downstream: noefor-upstream: yes: só o próprio Unbound consulta a cópia, e ele valida tudo com DNSSEC antes de responder ao cliente. - Ligue
fallback-enabled: yes. Se a cópia falhar ou expirar, o Unbound volta para a raiz da internet. - Ligue
zonemd-check: yespara conferir o resumo criptográfico da zona inteira. - Não espere navegação mais rápida. O ganho real é resiliência e menos consultas saindo para a raiz.
O que é DNS hyperlocal
Um recursivo sem nada em cache começa pela raiz. Ele pergunta a um dos 13 servidores raiz (letras de a a m, com instâncias anycast pelo mundo) e recebe a delegação do TLD. Depois pergunta ao TLD e por fim ao autoritativo. No DNS hyperlocal, o primeiro passo não sai do servidor: a zona raiz inteira, com as assinaturas DNSSEC, está carregada na memória do Unbound.
A RFC 8806 pede três coisas. O sistema precisa validar com DNSSEC todos os registros assinados da zona. O serviço local da raiz só pode responder ao resolvedor do próprio host, nunca a outros sistemas. E a cópia precisa ser renovada pelos tempos do SOA; se não for possível renovar antes do expire, o resolvedor tem de voltar imediatamente para os servidores raiz da internet.
O Unbound atende os três requisitos com o auth-zone. Com for-downstream: no, ele não responde como autoritativo da raiz para ninguém. Com for-upstream: yes, ele usa a cópia no lugar da raiz durante a recursão, valida e guarda o resultado no cache. É o modo que a própria documentação do Unbound chama de comportamento hyperlocal.

O que você ganha de verdade
A RFC é honesta sobre isso: o efeito na velocidade das respostas boas é pequeno. As delegações dos TLDs têm TTL de cerca de dois dias e já estão no cache de qualquer recursivo com tráfego. O ganho está em outro lugar:
- Resiliência. Se a raiz ficar inalcançável por ataque ou por problema no seu trânsito, o recursivo continua resolvendo TLDs enquanto a cópia for válida.
- Menos consultas saindo. Nomes com TLD inexistente (erros de digitação, equipamentos mal configurados, sufixos internos vazando) recebem NXDOMAIN da cópia local.
- Privacidade. Essas consultas deixam de atravessar a rede até a raiz.
O que pode dar errado
A RFC também lista o custo: é mais uma peça operacional. Se a renovação quebrar e ninguém notar, o recursivo pode ficar com dados velhos de um TLD que trocou todos os servidores de nome. Por isso o DNS hyperlocal só faz sentido com fallback ligado, validação DNSSEC e um alarme olhando o serial da cópia. Sem isso, você trocou um serviço que funciona sozinho por um que depende de você.
Configuração do DNS hyperlocal no Unbound
Crie o arquivo /etc/unbound/unbound.conf.d/hyperlocal.conf. O Debian já inclui todos os .conf desse diretório, então o restante da configuração do recursivo fica como está:
server:
extended-statistics: yes
auth-zone:
name: "."
primary: 170.247.170.2 # b.root-servers.net
primary: 192.33.4.12 # c.root-servers.net
primary: 199.7.91.13 # d.root-servers.net
primary: 192.5.5.241 # f.root-servers.net
primary: 192.112.36.4 # g.root-servers.net
primary: 193.0.14.129 # k.root-servers.net
primary: 192.0.47.132 # xfr.cjr.dns.icann.org
primary: 192.0.32.132 # xfr.lax.dns.icann.org
primary: 2801:1b8:10::b # b.root-servers.net
primary: 2001:500:2::c # c.root-servers.net
primary: 2001:500:2d::d # d.root-servers.net
primary: 2001:500:2f::f # f.root-servers.net
primary: 2001:500:12::d0d # g.root-servers.net
primary: 2001:7fd::1 # k.root-servers.net
primary: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org
primary: 2620:0:2d0:202::132 # xfr.lax.dns.icann.org
fallback-enabled: yes
for-downstream: no
for-upstream: yes
zonemd-check: yes
zonefile: "/var/lib/unbound/root.zone"| Opção | O que faz | Valor aqui |
|---|---|---|
name: "." | A zona é a raiz | Obrigatório |
primary | De onde baixar a zona por AXFR/IXFR; tenta o próximo se um falhar | Lista oficial do example.conf |
fallback-enabled | Se a busca na cópia falhar (inclusive por DNSSEC), consulta a internet | yes (padrão é no) |
for-downstream | Responder como autoritativo da raiz aos clientes | no (padrão é yes) |
for-upstream | Usar a cópia durante a recursão | yes |
zonemd-check | Conferir o registro ZONEMD, resumo da zona inteira | yes (padrão é no) |
zonefile | Arquivo onde a zona é gravada depois de cada transferência | Em /var/lib/unbound |
O bloco server: com extended-statistics liga os contadores que mostram quantas consultas a cópia respondeu. Sem ele, não há como medir o DNS hyperlocal depois. O Unbound aceita mais de um bloco server: espalhado pelos arquivos incluídos.
De onde vem a zona raiz
Nem todo servidor raiz aceita transferência de zona. A lista acima é a do example.conf do próprio Unbound: os servidores b, c, d, f, g e k, mais dois servidores de transferência da ICANN, em IPv4 e IPv6. O b.root-servers.net mudou de endereço em 2023; tutoriais antigos ainda trazem o IP anterior, que não deve mais ser usado como fonte. Confira a lista na versão atual do example.conf antes de copiar.
A transferência usa TCP na porta 53, saindo do recursivo. Se o seu firewall só libera UDP 53 de saída, o AXFR falha em silêncio e o fallback esconde o problema. O {a(U_UB, “manual do Unbound”)} recomenda IP nos primary, não nome, para não depender de resolver um nome que está dentro da própria zona. Há também a opção url:, que baixa o arquivo da zona por HTTP ou HTTPS; a RFC 8806 lista fontes possíveis no apêndice A.
Sobre integridade: a zona raiz é assinada, e o Unbound valida os registros com DNSSEC antes de usar. O zonemd-check soma uma conferência da zona inteira pelo registro ZONEMD, que pega alteração até na cola (glue), que não é assinada. Se a conferência falhar, a zona não é usada e, com o fallback ligado, a recursão segue pela raiz da internet.
unbound gravar só em arquivos dele dentro de /var/lib/unbound/. Um zonefile em /etc/unbound ou em outro diretório gera apparmor=”DENIED” no journal e a zona não é salva.Aplicar e conferir a transferência
unbound-checkconf systemctl restart unbound unbound-control list_auth_zones ls -l /var/lib/unbound/root.zone
O unbound-checkconf lê toda a configuração e acusa erro de sintaxe antes do restart. Depois do restart, o Unbound baixa a zona dos primary. O list_auth_zones mostra cada zona com o serial carregado:
. serial 2026102800
Versões mais novas acrescentam na mesma linha a data em que a zona foi obtida. Se aparecer expired ou no serial, a transferência não aconteceu: confira TCP 53 de saída e o journal com journalctl -u unbound. O ls -l deve mostrar o root.zone com alguns megabytes, dono unbound.
Testes com dig
dig @127.0.0.1 . SOA +short | cut -d' ' -f3 dig @170.247.170.2 . SOA +short | cut -d' ' -f3 dig @198.51.100.53 tld-que-nao-existe. A dig @198.51.100.53 registro.br A +dnssec
As duas primeiras linhas comparam o serial da cópia, perguntado ao próprio recursivo, com o serial de um servidor raiz. Os dois devem ser iguais ou a cópia ficar no máximo um passo atrás, porque o seu Unbound não recebe NOTIFY da raiz e só percebe a mudança quando confere o SOA, no intervalo de refresh (30 minutos na raiz):
2026102800 2026102800
O serial da raiz é a data mais um contador de dois dígitos, e muda pelo menos uma vez por dia. A terceira linha pergunta um TLD que não existe. A resposta tem de ser NXDOMAIN com a flag ad, sinal de que a negativa foi validada com DNSSEC:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 51344 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
A última linha é um teste de sanidade: um domínio real e assinado continua resolvendo com ad. Se o registro.br parar de resolver depois do DNS hyperlocal, desligue a configuração (seção mais abaixo) e investigue com calma.
Medir o ganho
unbound-control stats_noreset | grep authzone tcpdump -ni eth0 -c 20 'dst port 53 and src host 198.51.100.53'
num.query.authzone.up=18342 num.query.authzone.down=0
num.query.authzone.up: consultas internas da recursão respondidas pela cópia. Sem DNS hyperlocal, elas iriam para a internet.num.query.authzone.down: respostas dadas diretamente a clientes. Comfor-downstream: no, fica em zero.tcpdump: mostra as consultas que saem do recursivo para a porta 53. Com a cópia ativa, os destinos são servidores de TLD e autoritativos. Para os endereços da raiz sobram só as consultas de SOA e as transferências da própria cópia.
Compare com o número total de consultas (total.num.queries) para ter a proporção. Em um recursivo de provedor com cache quente, a fração que tocaria a raiz é pequena, e grande parte dela são nomes inexistentes. O Unbound já reduz essas consultas com o aggressive-nsec (RFC 8198), ligado por padrão, que reaproveita as provas NSEC de respostas anteriores. Meça antes e depois: a decisão de manter o DNS hyperlocal deve vir desse número, não de promessa de velocidade.
Em recursivos anycast, como no cenário de DNS anycast com BGP e FRR, cada nó mantém a sua própria cópia. Vale conferir o serial em todos os nós, porque um nó com transferência quebrada fica escondido atrás do mesmo IP de serviço.
Vigiar a cópia da raiz
A RFC 8806 recomenda um método automático para confirmar que a cópia está sendo renovada, e sugere olhar o serial do SOA. O script abaixo lê o serial pelo unbound-control, pega a data (os oito primeiros dígitos) e reclama se ela tiver mais de dois dias:
#!/bin/sh
# /usr/local/bin/raiz-local: avisa se a cópia da raiz parou de atualizar
S=$(unbound-control list_auth_zones | awk '$1 == "." {print $3}')
case "$S" in
[0-9]*) ;;
*) echo "raiz local sem serial"; exit 2 ;;
esac
DIA=$(echo "$S" | cut -c1-8)
IDADE=$(( ($(date -u +%s) - $(date -u -d "$DIA" +%s)) / 86400 ))
if [ "$IDADE" -gt 2 ]; then
echo "raiz local parada: serial $S, $IDADE dias"
exit 1
fi
echo "ok: serial $S"O awk pega o terceiro campo da linha da zona ., que é o número do serial. O date -d converte a data AAAAMMDD em segundos, e a diferença dividida por 86400 dá a idade em dias. A saída usa códigos de retorno, então o script entra fácil num item do Zabbix ou num cron que manda e-mail. O expire do SOA da raiz é de sete dias; o alarme em dois dias dá tempo de agir antes do fallback.
Para forçar uma nova verificação nos primary sem reiniciar o serviço:
unbound-control auth_zone_transfer . unbound-control list_auth_zones
Como desligar o DNS hyperlocal
Se algo der errado, o caminho de volta é tirar o arquivo e reiniciar. O resto do recursivo não depende dele:
mv /etc/unbound/unbound.conf.d/hyperlocal.conf /root/hyperlocal.conf.off unbound-checkconf && systemctl restart unbound
O && só reinicia se a configuração restante estiver válida. Depois do restart, o Unbound volta a usar os servidores raiz pelas root hints embutidas, como antes.
Erros comuns
- Lista de primary desatualizada. IP antigo do b.root ou servidor que não aceita AXFR. Use a lista do example.conf da versão atual.
- TCP 53 bloqueado na saída. A transferência falha e o fallback mascara. O
list_auth_zonesdenuncia. - for-downstream ligado. O padrão é
yes. Esquecer a linha faz o Unbound responder como autoritativo da raiz aos clientes, o que a RFC 8806 proíbe. - Sem fallback. Com
fallback-enabled: no(padrão), cópia expirada vira SERVFAIL para todo mundo. - zonefile fora de /var/lib/unbound. O AppArmor bloqueia a gravação.
- Nenhum alarme. Sem vigiar o serial, a cópia pode ficar dias parada.
- Recursivo aberto. DNS hyperlocal não muda o access-control. Recursivo aberto para a internet continua sendo vetor de ataque, como explica o post sobre portas de amplificação DDoS.
Perguntas frequentes
O DNS hyperlocal deixa a navegação mais rápida?
Quase nada. As delegações dos TLDs ficam em cache por cerca de dois dias, então a raiz é pouco consultada num recursivo com tráfego. O ganho real é resiliência e menos consultas saindo para a raiz, principalmente de nomes inexistentes.
Quanto espaço e memória a zona raiz ocupa?
O arquivo tem alguns megabytes, com as assinaturas DNSSEC incluídas. Para um servidor que já roda um recursivo de provedor, o consumo extra de disco e memória é pequeno.
O que acontece se a transferência da raiz parar?
O Unbound continua usando a cópia até o expire do SOA, que na raiz é de sete dias. Depois disso a zona expira e, com fallback-enabled ligado, o Unbound volta a consultar os servidores raiz. Sem fallback, as consultas viram SERVFAIL.
Posso usar o DNS hyperlocal com BIND ou outro resolvedor?
Pode. A RFC 8806 traz exemplos para vários resolvedores no apêndice B, e o BIND faz o mesmo com zona secundária e mirror. Este post cobre só o Unbound, que resolve tudo dentro do auth-zone.
Preciso de DNSSEC ligado para usar a zona raiz local?
Sim. A RFC exige que o sistema valide todos os registros assinados da zona e tenha a chave KSK da raiz atualizada. No Debian, o auto-trust-anchor-file do pacote já cuida da chave.
Conclusão
O DNS hyperlocal no Unbound é um arquivo de configuração de vinte e poucas linhas, mas exige disciplina: lista de fontes atualizada, TCP 53 liberado, fallback ligado, ZONEMD conferido e um alarme no serial. Em troca, o recursivo continua resolvendo TLDs se a raiz ficar inalcançável e deixa de mandar para fora as consultas a TLDs que não existem. Meça com os contadores do authzone antes de decidir se fica.
Referências: RFC 8806, unbound.conf(5) e o example.conf do Unbound.
Leitura recomendada: Tutorial DNS Hyperlocal, da wiki do Brasil Peering Forum (CC BY-NC 4.0).
